diff options
68 files changed, 599 insertions, 851 deletions
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index c857d54925..d919243e6b 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -223,59 +223,44 @@ String FileAccess::fix_path(const String &p_path) const { } /* these are all implemented for ease of porting, then can later be optimized */ +uint8_t FileAccess::get_8() const { + uint8_t data = 0; + get_buffer(&data, sizeof(uint8_t)); -uint16_t FileAccess::get_16() const { - uint16_t res; - uint8_t a, b; + return data; +} - a = get_8(); - b = get_8(); +uint16_t FileAccess::get_16() const { + uint16_t data = 0; + get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint16_t)); if (big_endian) { - SWAP(a, b); + data = BSWAP16(data); } - res = b; - res <<= 8; - res |= a; - - return res; + return data; } uint32_t FileAccess::get_32() const { - uint32_t res; - uint16_t a, b; - - a = get_16(); - b = get_16(); + uint32_t data = 0; + get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint32_t)); if (big_endian) { - SWAP(a, b); + data = BSWAP32(data); } - res = b; - res <<= 16; - res |= a; - - return res; + return data; } uint64_t FileAccess::get_64() const { - uint64_t res; - uint32_t a, b; - - a = get_32(); - b = get_32(); + uint64_t data = 0; + get_buffer(reinterpret_cast<uint8_t *>(&data), sizeof(uint64_t)); if (big_endian) { - SWAP(a, b); + data = BSWAP64(data); } - res = b; - res <<= 32; - res |= a; - - return res; + return data; } float FileAccess::get_float() const { @@ -465,17 +450,6 @@ String FileAccess::get_as_text(bool p_skip_cr) const { return text; } -uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const { - ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); - - uint64_t i = 0; - for (i = 0; i < p_length && !eof_reached(); i++) { - p_dst[i] = get_8(); - } - - return i; -} - Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const { Vector<uint8_t> data; @@ -488,7 +462,7 @@ Vector<uint8_t> FileAccess::get_buffer(int64_t p_length) const { ERR_FAIL_COND_V_MSG(err != OK, data, "Can't resize data to " + itos(p_length) + " elements."); uint8_t *w = data.ptrw(); - int64_t len = get_buffer(&w[0], p_length); + int64_t len = get_buffer(w, p_length); if (len < p_length) { data.resize(len); @@ -512,46 +486,32 @@ String FileAccess::get_as_utf8_string(bool p_skip_cr) const { return s; } -void FileAccess::store_16(uint16_t p_dest) { - uint8_t a, b; - - a = p_dest & 0xFF; - b = p_dest >> 8; +void FileAccess::store_8(uint8_t p_dest) { + store_buffer(&p_dest, sizeof(uint8_t)); +} +void FileAccess::store_16(uint16_t p_dest) { if (big_endian) { - SWAP(a, b); + p_dest = BSWAP16(p_dest); } - store_8(a); - store_8(b); + store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint16_t)); } void FileAccess::store_32(uint32_t p_dest) { - uint16_t a, b; - - a = p_dest & 0xFFFF; - b = p_dest >> 16; - if (big_endian) { - SWAP(a, b); + p_dest = BSWAP32(p_dest); } - store_16(a); - store_16(b); + store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint32_t)); } void FileAccess::store_64(uint64_t p_dest) { - uint32_t a, b; - - a = p_dest & 0xFFFFFFFF; - b = p_dest >> 32; - if (big_endian) { - SWAP(a, b); + p_dest = BSWAP64(p_dest); } - store_32(a); - store_32(b); + store_buffer(reinterpret_cast<uint8_t *>(&p_dest), sizeof(uint64_t)); } void FileAccess::store_real(real_t p_real) { @@ -708,22 +668,11 @@ void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_ store_line(line); } -void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) { - ERR_FAIL_COND(!p_src && p_length > 0); - for (uint64_t i = 0; i < p_length; i++) { - store_8(p_src[i]); - } -} - void FileAccess::store_buffer(const Vector<uint8_t> &p_buffer) { uint64_t len = p_buffer.size(); - if (len == 0) { - return; - } - const uint8_t *r = p_buffer.ptr(); - store_buffer(&r[0], len); + store_buffer(r, len); } void FileAccess::store_var(const Variant &p_var, bool p_full_objects) { diff --git a/core/io/file_access.h b/core/io/file_access.h index 2ab84db4b6..2f4d1a8604 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -137,7 +137,7 @@ public: virtual bool eof_reached() const = 0; ///< reading passed EOF - virtual uint8_t get_8() const = 0; ///< get a byte + virtual uint8_t get_8() const; ///< get a byte virtual uint16_t get_16() const; ///< get 16 bits uint virtual uint32_t get_32() const; ///< get 32 bits uint virtual uint64_t get_64() const; ///< get 64 bits uint @@ -148,7 +148,7 @@ public: Variant get_var(bool p_allow_objects = false) const; - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const; ///< get an array of bytes + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const = 0; ///< get an array of bytes, needs to be overwritten by children. Vector<uint8_t> get_buffer(int64_t p_length) const; virtual String get_line() const; virtual String get_token() const; @@ -168,7 +168,7 @@ public: virtual Error resize(int64_t p_length) = 0; virtual void flush() = 0; - virtual void store_8(uint8_t p_dest) = 0; ///< store a byte + virtual void store_8(uint8_t p_dest); ///< store a byte virtual void store_16(uint16_t p_dest); ///< store 16 bits uint virtual void store_32(uint32_t p_dest); ///< store 32 bits uint virtual void store_64(uint64_t p_dest); ///< store 64 bits uint @@ -184,7 +184,7 @@ public: virtual void store_pascal_string(const String &p_string); virtual String get_pascal_string(); - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length); ///< store an array of bytes + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) = 0; ///< store an array of bytes, needs to be overwritten by children. void store_buffer(const Vector<uint8_t> &p_buffer); void store_var(const Variant &p_var, bool p_full_objects = false); diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp index 0f00bd292c..3602baf8c5 100644 --- a/core/io/file_access_compressed.cpp +++ b/core/io/file_access_compressed.cpp @@ -260,38 +260,6 @@ bool FileAccessCompressed::eof_reached() const { } } -uint8_t FileAccessCompressed::get_8() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); - ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); - - if (at_end) { - read_eof = true; - return 0; - } - - uint8_t ret = read_ptr[read_pos]; - - read_pos++; - if (read_pos >= read_block_size) { - read_block++; - - if (read_block < read_block_count) { - //read another block of compressed data - f->get_buffer(comp_buffer.ptrw(), read_blocks[read_block].csize); - int total = Compression::decompress(buffer.ptrw(), read_blocks.size() == 1 ? read_total : block_size, comp_buffer.ptr(), read_blocks[read_block].csize, cmode); - ERR_FAIL_COND_V_MSG(total == -1, 0, "Compressed file is corrupt."); - read_block_size = read_block == read_block_count - 1 ? read_total % block_size : block_size; - read_pos = 0; - - } else { - read_block--; - at_end = true; - } - } - - return ret; -} - uint64_t FileAccessCompressed::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use."); @@ -341,12 +309,13 @@ void FileAccessCompressed::flush() { // compressed files keep data in memory till close() } -void FileAccessCompressed::store_8(uint8_t p_dest) { +void FileAccessCompressed::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND_MSG(f.is_null(), "File must be opened before use."); ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); - WRITE_FIT(1); - write_ptr[write_pos++] = p_dest; + WRITE_FIT(p_length); + memcpy(write_ptr + write_pos, p_src, p_length); + write_pos += p_length; } bool FileAccessCompressed::file_exists(const String &p_name) { diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index f706c82f8e..ea9837dd03 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -83,14 +83,13 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp index b689f5b628..605c5b2f6f 100644 --- a/core/io/file_access_encrypted.cpp +++ b/core/io/file_access_encrypted.cpp @@ -206,26 +206,13 @@ bool FileAccessEncrypted::eof_reached() const { return eofed; } -uint8_t FileAccessEncrypted::get_8() const { - ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode."); - if (pos >= get_length()) { - eofed = true; - return 0; - } - - uint8_t b = data[pos]; - pos++; - return b; -} - uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode."); uint64_t to_copy = MIN(p_length, get_length() - pos); - for (uint64_t i = 0; i < to_copy; i++) { - p_dst[i] = data[pos++]; - } + memcpy(p_dst, data.ptr() + pos, to_copy); + pos += to_copy; if (to_copy < p_length) { eofed = true; @@ -242,17 +229,12 @@ void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); ERR_FAIL_COND(!p_src && p_length > 0); - if (pos < get_length()) { - for (uint64_t i = 0; i < p_length; i++) { - store_8(p_src[i]); - } - } else if (pos == get_length()) { + if (pos + p_length >= get_length()) { data.resize(pos + p_length); - for (uint64_t i = 0; i < p_length; i++) { - data.write[pos + i] = p_src[i]; - } - pos += p_length; } + + memcpy(data.ptrw() + pos, p_src, p_length); + pos += p_length; } void FileAccessEncrypted::flush() { @@ -261,18 +243,6 @@ void FileAccessEncrypted::flush() { // encrypted files keep data in memory till close() } -void FileAccessEncrypted::store_8(uint8_t p_dest) { - ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode."); - - if (pos < get_length()) { - data.write[pos] = p_dest; - pos++; - } else if (pos == get_length()) { - data.push_back(p_dest); - pos++; - } -} - bool FileAccessEncrypted::file_exists(const String &p_name) { Ref<FileAccess> fa = FileAccess::open(p_name, FileAccess::READ); if (fa.is_null()) { diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 42afe49a5e..5f8c803d60 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -73,14 +73,12 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 9521a4f666..1541a5ed4a 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -122,16 +122,6 @@ bool FileAccessMemory::eof_reached() const { return pos >= length; } -uint8_t FileAccessMemory::get_8() const { - uint8_t ret = 0; - if (pos < length) { - ret = data[pos]; - } - ++pos; - - return ret; -} - uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V(data, -1); @@ -157,16 +147,12 @@ void FileAccessMemory::flush() { ERR_FAIL_NULL(data); } -void FileAccessMemory::store_8(uint8_t p_byte) { - ERR_FAIL_NULL(data); - ERR_FAIL_COND(pos >= length); - data[pos++] = p_byte; -} - void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND(!p_src && p_length > 0); + uint64_t left = length - pos; uint64_t write = MIN(p_length, left); + if (write < p_length) { WARN_PRINT("Writing less data than requested"); } diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index e9fbc26d75..39e1528d97 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -55,15 +55,12 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_8(uint8_t p_byte) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 02bf0a6039..eec27ce0aa 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -313,17 +313,6 @@ bool FileAccessPack::eof_reached() const { return eof; } -uint8_t FileAccessPack::get_8() const { - ERR_FAIL_COND_V_MSG(f.is_null(), 0, "File must be opened before use."); - if (pos >= pf.size) { - eof = true; - return 0; - } - - pos++; - return f->get_8(); -} - uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V_MSG(f.is_null(), -1, "File must be opened before use."); ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); @@ -366,10 +355,6 @@ void FileAccessPack::flush() { ERR_FAIL(); } -void FileAccessPack::store_8(uint8_t p_dest) { - ERR_FAIL(); -} - void FileAccessPack::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL(); } diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 594ac8f089..595a36bca4 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -169,8 +169,6 @@ public: virtual bool eof_reached() const override; - virtual uint8_t get_8() const override; - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual void set_big_endian(bool p_big_endian) override; @@ -179,8 +177,6 @@ public: virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index c0d1afc8e1..b33b7b35c3 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -291,12 +291,6 @@ bool FileAccessZip::eof_reached() const { return at_eof; } -uint8_t FileAccessZip::get_8() const { - uint8_t ret = 0; - get_buffer(&ret, 1); - return ret; -} - uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V(zfile, -1); @@ -328,7 +322,7 @@ void FileAccessZip::flush() { ERR_FAIL(); } -void FileAccessZip::store_8(uint8_t p_dest) { +void FileAccessZip::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL(); } diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 88b63e93e2..1e11e050df 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -95,14 +95,13 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index dc1bee4336..d762ffa5a6 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -376,7 +376,19 @@ </constant> <constant name="ANIMATION_CALLBACK_MODE_DISCRETE_FORCE_CONTINUOUS" value="2" enum="AnimationCallbackModeDiscrete"> Always treat the [constant Animation.UPDATE_DISCRETE] track value as [constant Animation.UPDATE_CONTINUOUS] with [constant Animation.INTERPOLATION_NEAREST]. This is the default behavior for [AnimationTree]. - If a value track has non-numeric type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE] with [constant Animation.UPDATE_DISCRETE]. + If a value track has un-interpolatable type key values, it is internally converted to use [constant ANIMATION_CALLBACK_MODE_DISCRETE_RECESSIVE] with [constant Animation.UPDATE_DISCRETE]. + Un-interpolatable type list: + - [constant @GlobalScope.TYPE_NIL] + - [constant @GlobalScope.TYPE_NODE_PATH] + - [constant @GlobalScope.TYPE_RID] + - [constant @GlobalScope.TYPE_OBJECT] + - [constant @GlobalScope.TYPE_CALLABLE] + - [constant @GlobalScope.TYPE_SIGNAL] + - [constant @GlobalScope.TYPE_DICTIONARY] + - [constant @GlobalScope.TYPE_PACKED_BYTE_ARRAY] + [constant @GlobalScope.TYPE_BOOL] and [constant @GlobalScope.TYPE_INT] are treated as [constant @GlobalScope.TYPE_FLOAT] during blending and rounded when the result is retrieved. + It is same for arrays and vectors with them such as [constant @GlobalScope.TYPE_PACKED_INT32_ARRAY] or [constant @GlobalScope.TYPE_VECTOR2I], they are treated as [constant @GlobalScope.TYPE_PACKED_FLOAT32_ARRAY] or [constant @GlobalScope.TYPE_VECTOR2]. Also note that for arrays, the size is also interpolated. + [constant @GlobalScope.TYPE_STRING] and [constant @GlobalScope.TYPE_STRING_NAME] are interpolated between character codes and lengths, but note that there is a difference in algorithm between interpolation between keys and interpolation by blending. </constant> </constants> </class> diff --git a/doc/classes/EditorResourcePreviewGenerator.xml b/doc/classes/EditorResourcePreviewGenerator.xml index fcfdbb5c44..9c9b6d11b2 100644 --- a/doc/classes/EditorResourcePreviewGenerator.xml +++ b/doc/classes/EditorResourcePreviewGenerator.xml @@ -23,7 +23,7 @@ <param index="2" name="metadata" type="Dictionary" /> <description> Generate a preview from a given resource with the specified size. This must always be implemented. - Returning an empty texture is an OK way to fail and let another generator take care. + Returning [code]null[/code] is an OK way to fail and let another generator take care. Care must be taken because this function is always called from a thread (not the main thread). [param metadata] dictionary can be modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). </description> @@ -35,7 +35,7 @@ <param index="2" name="metadata" type="Dictionary" /> <description> Generate a preview directly from a path with the specified size. Implementing this is optional, as default code will load and call [method _generate]. - Returning an empty texture is an OK way to fail and let another generator take care. + Returning [code]null[/code] is an OK way to fail and let another generator take care. Care must be taken because this function is always called from a thread (not the main thread). [param metadata] dictionary can be modified to store file-specific metadata that can be used in [method EditorResourceTooltipPlugin._make_tooltip_for_path] (like image size, sample length etc.). </description> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index c81d5d4fab..4cdfba17e9 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -412,6 +412,14 @@ [b]Note:[/b] [param count] is unused and can be left unspecified. </description> </method> + <method name="canvas_item_attach_skeleton"> + <return type="void" /> + <param index="0" name="item" type="RID" /> + <param index="1" name="skeleton" type="RID" /> + <description> + Attaches a skeleton to the [CanvasItem]. Removes the previous skeleton. + </description> + </method> <method name="canvas_item_clear"> <return type="void" /> <param index="0" name="item" type="RID" /> diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 57fe96fb6f..36393dde86 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1389,8 +1389,22 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { tinfo.format = t->format; tinfo.width = t->alloc_width; tinfo.height = t->alloc_height; - tinfo.depth = t->depth; tinfo.bytes = t->total_data_size; + + switch (t->type) { + case Texture::TYPE_3D: + tinfo.depth = t->depth; + break; + + case Texture::TYPE_LAYERED: + tinfo.depth = t->layers; + break; + + default: + tinfo.depth = 0; + break; + } + r_info->push_back(tinfo); } } @@ -1521,7 +1535,11 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref<Image> &p_image, h = MAX(1, h >> 1); } - texture->total_data_size = tsize; + if (texture->target == GL_TEXTURE_CUBE_MAP || texture->target == GL_TEXTURE_2D_ARRAY) { + texture->total_data_size = tsize * texture->layers; + } else { + texture->total_data_size = tsize; + } texture->stored_cube_sides |= (1 << p_layer); diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index ea8b42b2e4..32f2d7dd79 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -218,67 +218,13 @@ bool FileAccessUnix::eof_reached() const { return last_error == ERR_FILE_EOF; } -uint8_t FileAccessUnix::get_8() const { - ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); - uint8_t b; - if (fread(&b, 1, 1, f) == 0) { - check_errors(); - b = '\0'; - } - return b; -} - -uint16_t FileAccessUnix::get_16() const { - ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); - - uint16_t b = 0; - if (fread(&b, 1, 2, f) != 2) { - check_errors(); - } - - if (big_endian) { - b = BSWAP16(b); - } - - return b; -} - -uint32_t FileAccessUnix::get_32() const { - ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); - - uint32_t b = 0; - if (fread(&b, 1, 4, f) != 4) { - check_errors(); - } - - if (big_endian) { - b = BSWAP32(b); - } - - return b; -} - -uint64_t FileAccessUnix::get_64() const { - ERR_FAIL_NULL_V_MSG(f, 0, "File must be opened before use."); - - uint64_t b = 0; - if (fread(&b, 1, 8, f) != 8) { - check_errors(); - } - - if (big_endian) { - b = BSWAP64(b); - } - - return b; -} - uint64_t FileAccessUnix::get_buffer(uint8_t *p_dst, uint64_t p_length) const { - ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V_MSG(f, -1, "File must be opened before use."); + ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); + return read; } @@ -308,41 +254,6 @@ void FileAccessUnix::flush() { fflush(f); } -void FileAccessUnix::store_8(uint8_t p_dest) { - ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - ERR_FAIL_COND(fwrite(&p_dest, 1, 1, f) != 1); -} - -void FileAccessUnix::store_16(uint16_t p_dest) { - ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - - if (big_endian) { - p_dest = BSWAP16(p_dest); - } - - ERR_FAIL_COND(fwrite(&p_dest, 1, 2, f) != 2); -} - -void FileAccessUnix::store_32(uint32_t p_dest) { - ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - - if (big_endian) { - p_dest = BSWAP32(p_dest); - } - - ERR_FAIL_COND(fwrite(&p_dest, 1, 4, f) != 4); -} - -void FileAccessUnix::store_64(uint64_t p_dest) { - ERR_FAIL_NULL_MSG(f, "File must be opened before use."); - - if (big_endian) { - p_dest = BSWAP64(p_dest); - } - - ERR_FAIL_COND(fwrite(&p_dest, 1, 8, f) != 8); -} - void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); ERR_FAIL_COND(!p_src && p_length > 0); diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index c0286dbff3..76f629f7c2 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -67,20 +67,12 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte - virtual uint16_t get_16() const override; - virtual uint32_t get_32() const override; - virtual uint64_t get_64() const override; virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override; virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte - virtual void store_16(uint16_t p_dest) override; - virtual void store_32(uint32_t p_dest) override; - virtual void store_64(uint64_t p_dest) override; virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_path) override; ///< return true if a file exists diff --git a/drivers/unix/file_access_unix_pipe.cpp b/drivers/unix/file_access_unix_pipe.cpp index 5d9a27ad05..34758e8c7d 100644 --- a/drivers/unix/file_access_unix_pipe.cpp +++ b/drivers/unix/file_access_unix_pipe.cpp @@ -125,22 +125,9 @@ String FileAccessUnixPipe::get_path_absolute() const { return path_src; } -uint8_t FileAccessUnixPipe::get_8() const { - ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use."); - - uint8_t b; - if (::read(fd[0], &b, 1) == 0) { - last_error = ERR_FILE_CANT_READ; - b = '\0'; - } else { - last_error = OK; - } - return b; -} - uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const { - ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use."); + ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); uint64_t read = ::read(fd[0], p_dst, p_length); if (read == p_length) { @@ -155,18 +142,10 @@ Error FileAccessUnixPipe::get_error() const { return last_error; } -void FileAccessUnixPipe::store_8(uint8_t p_src) { - ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use."); - if (::write(fd[1], &p_src, 1) != 1) { - last_error = ERR_FILE_CANT_WRITE; - } else { - last_error = OK; - } -} - void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use."); ERR_FAIL_COND(!p_src && p_length > 0); + if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) { last_error = ERR_FILE_CANT_WRITE; } else { diff --git a/drivers/unix/file_access_unix_pipe.h b/drivers/unix/file_access_unix_pipe.h index 8e7988791b..19acdb5a37 100644 --- a/drivers/unix/file_access_unix_pipe.h +++ b/drivers/unix/file_access_unix_pipe.h @@ -65,14 +65,12 @@ public: virtual bool eof_reached() const override { return false; } - virtual uint8_t get_8() const override; ///< get a byte virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override {} - virtual void store_8(uint8_t p_src) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_path) override { return false; } diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index 2ba353868b..4ea46e8214 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -497,6 +497,7 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() { _register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); _register_requested_device_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, false); _register_requested_device_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false); + _register_requested_device_extension(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME, false); if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { _register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); @@ -1705,6 +1706,16 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat & image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; } + VkImageViewASTCDecodeModeEXT decode_mode; + if (enabled_device_extension_names.has(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME)) { + if (image_view_create_info.format >= VK_FORMAT_ASTC_4x4_UNORM_BLOCK && image_view_create_info.format <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK) { + decode_mode.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT; + decode_mode.pNext = nullptr; + decode_mode.decodeMode = VK_FORMAT_R8G8B8A8_UNORM; + image_view_create_info.pNext = &decode_mode; + } + } + VkImageView vk_image_view = VK_NULL_HANDLE; err = vkCreateImageView(vk_device, &image_view_create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_IMAGE_VIEW), &vk_image_view); if (err) { diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index f6f721639c..0243d863f8 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -323,93 +323,9 @@ bool FileAccessWindows::eof_reached() const { return last_error == ERR_FILE_EOF; } -uint8_t FileAccessWindows::get_8() const { - ERR_FAIL_NULL_V(f, 0); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == WRITE) { - fflush(f); - } - prev_op = READ; - } - uint8_t b; - if (fread(&b, 1, 1, f) == 0) { - check_errors(); - b = '\0'; - } - - return b; -} - -uint16_t FileAccessWindows::get_16() const { - ERR_FAIL_NULL_V(f, 0); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == WRITE) { - fflush(f); - } - prev_op = READ; - } - - uint16_t b = 0; - if (fread(&b, 1, 2, f) != 2) { - check_errors(); - } - - if (big_endian) { - b = BSWAP16(b); - } - - return b; -} - -uint32_t FileAccessWindows::get_32() const { - ERR_FAIL_NULL_V(f, 0); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == WRITE) { - fflush(f); - } - prev_op = READ; - } - - uint32_t b = 0; - if (fread(&b, 1, 4, f) != 4) { - check_errors(); - } - - if (big_endian) { - b = BSWAP32(b); - } - - return b; -} - -uint64_t FileAccessWindows::get_64() const { - ERR_FAIL_NULL_V(f, 0); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == WRITE) { - fflush(f); - } - prev_op = READ; - } - - uint64_t b = 0; - if (fread(&b, 1, 8, f) != 8) { - check_errors(); - } - - if (big_endian) { - b = BSWAP64(b); - } - - return b; -} - uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const { - ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_NULL_V(f, -1); + ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); if (flags == READ_WRITE || flags == WRITE_READ) { if (prev_op == WRITE) { @@ -417,8 +333,10 @@ uint64_t FileAccessWindows::get_buffer(uint8_t *p_dst, uint64_t p_length) const } prev_op = READ; } + uint64_t read = fread(p_dst, 1, p_length, f); check_errors(); + return read; } @@ -453,77 +371,6 @@ void FileAccessWindows::flush() { } } -void FileAccessWindows::store_8(uint8_t p_dest) { - ERR_FAIL_NULL(f); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == READ) { - if (last_error != ERR_FILE_EOF) { - fseek(f, 0, SEEK_CUR); - } - } - prev_op = WRITE; - } - fwrite(&p_dest, 1, 1, f); -} - -void FileAccessWindows::store_16(uint16_t p_dest) { - ERR_FAIL_NULL(f); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == READ) { - if (last_error != ERR_FILE_EOF) { - fseek(f, 0, SEEK_CUR); - } - } - prev_op = WRITE; - } - - if (big_endian) { - p_dest = BSWAP16(p_dest); - } - - fwrite(&p_dest, 1, 2, f); -} - -void FileAccessWindows::store_32(uint32_t p_dest) { - ERR_FAIL_NULL(f); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == READ) { - if (last_error != ERR_FILE_EOF) { - fseek(f, 0, SEEK_CUR); - } - } - prev_op = WRITE; - } - - if (big_endian) { - p_dest = BSWAP32(p_dest); - } - - fwrite(&p_dest, 1, 4, f); -} - -void FileAccessWindows::store_64(uint64_t p_dest) { - ERR_FAIL_NULL(f); - - if (flags == READ_WRITE || flags == WRITE_READ) { - if (prev_op == READ) { - if (last_error != ERR_FILE_EOF) { - fseek(f, 0, SEEK_CUR); - } - } - prev_op = WRITE; - } - - if (big_endian) { - p_dest = BSWAP64(p_dest); - } - - fwrite(&p_dest, 1, 8, f); -} - void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_NULL(f); ERR_FAIL_COND(!p_src && p_length > 0); @@ -536,6 +383,7 @@ void FileAccessWindows::store_buffer(const uint8_t *p_src, uint64_t p_length) { } prev_op = WRITE; } + ERR_FAIL_COND(fwrite(p_src, 1, p_length, f) != (size_t)p_length); } @@ -559,11 +407,10 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) { return 0; } - String file = p_file; - if (file.ends_with("/") && file != "/") { + String file = fix_path(p_file); + if (file.ends_with("\\") && file != "\\") { file = file.substr(0, file.length() - 1); } - file = fix_path(file); struct _stat st; int rv = _wstat((LPCWSTR)(file.utf16().get_data()), &st); diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index a25bbcfb3a..f458ff9c6c 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -69,20 +69,12 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF - virtual uint8_t get_8() const override; ///< get a byte - virtual uint16_t get_16() const override; - virtual uint32_t get_32() const override; - virtual uint64_t get_64() const override; virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override; virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte - virtual void store_16(uint16_t p_dest) override; - virtual void store_32(uint32_t p_dest) override; - virtual void store_64(uint64_t p_dest) override; virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override; ///< return true if a file exists diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp index 7902c8e1d8..0c953b14aa 100644 --- a/drivers/windows/file_access_windows_pipe.cpp +++ b/drivers/windows/file_access_windows_pipe.cpp @@ -96,22 +96,9 @@ String FileAccessWindowsPipe::get_path_absolute() const { return path_src; } -uint8_t FileAccessWindowsPipe::get_8() const { - ERR_FAIL_COND_V_MSG(fd[0] == 0, 0, "Pipe must be opened before use."); - - uint8_t b; - if (!ReadFile(fd[0], &b, 1, nullptr, nullptr)) { - last_error = ERR_FILE_CANT_READ; - b = '\0'; - } else { - last_error = OK; - } - return b; -} - uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const { - ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use."); + ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); DWORD read = -1; if (!ReadFile(fd[0], p_dst, p_length, &read, nullptr) || read != p_length) { @@ -126,15 +113,6 @@ Error FileAccessWindowsPipe::get_error() const { return last_error; } -void FileAccessWindowsPipe::store_8(uint8_t p_src) { - ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use."); - if (!WriteFile(fd[1], &p_src, 1, nullptr, nullptr)) { - last_error = ERR_FILE_CANT_WRITE; - } else { - last_error = OK; - } -} - void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use."); ERR_FAIL_COND(!p_src && p_length > 0); diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h index b885ef78e6..4e9bd036ae 100644 --- a/drivers/windows/file_access_windows_pipe.h +++ b/drivers/windows/file_access_windows_pipe.h @@ -64,14 +64,12 @@ public: virtual bool eof_reached() const override { return false; } - virtual uint8_t get_8() const override; ///< get a byte virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override {} - virtual void store_8(uint8_t p_src) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes virtual bool file_exists(const String &p_name) override { return false; } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 8c07cefc19..80bf9f850e 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -4554,6 +4554,10 @@ bool AnimationTrackEditor::is_snap_enabled() const { return snap->is_pressed() ^ Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); } +bool AnimationTrackEditor::is_bezier_editor_active() const { + return bezier_edit->is_visible(); +} + bool AnimationTrackEditor::can_add_reset_key() const { for (const KeyValue<SelectedKey, KeyInfo> &E : selection) { const Animation::TrackType track_type = animation->track_get_type(E.key.track); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index a517ba8b43..516ddb0154 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -729,6 +729,7 @@ public: bool is_key_clipboard_active() const; bool is_moving_selection() const; bool is_snap_enabled() const; + bool is_bezier_editor_active() const; bool can_add_reset_key() const; float get_moving_selection_offset() const; float snap_time(float p_value, bool p_relative = false); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 8935b9ad8a..f20dd992bb 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -175,6 +175,13 @@ void EditorResourcePicker::_file_quick_selected() { _file_selected(quick_open->get_selected()); } +void EditorResourcePicker::_resource_saved(Object *p_resource) { + if (edited_resource.is_valid() && p_resource == edited_resource.ptr()) { + emit_signal(SNAME("resource_changed"), edited_resource); + _update_resource(); + } +} + void EditorResourcePicker::_update_menu() { _update_menu_items(); @@ -408,6 +415,10 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { if (edited_resource.is_null()) { return; } + Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved); + if (!EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) { + EditorNode::get_singleton()->connect("resource_saved", resource_saved); + } EditorNode::get_singleton()->save_resource_as(edited_resource); } break; @@ -833,6 +844,13 @@ void EditorResourcePicker::_notification(int p_what) { assign_button->queue_redraw(); } } break; + + case NOTIFICATION_EXIT_TREE: { + Callable resource_saved = callable_mp(this, &EditorResourcePicker::_resource_saved); + if (EditorNode::get_singleton()->is_connected("resource_saved", resource_saved)) { + EditorNode::get_singleton()->disconnect("resource_saved", resource_saved); + } + } break; } } diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 28229e6b37..05e392da2c 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -91,6 +91,8 @@ class EditorResourcePicker : public HBoxContainer { void _file_quick_selected(); void _file_selected(const String &p_path); + void _resource_saved(Object *p_resource); + void _update_menu(); void _update_menu_items(); void _edit_menu_cbk(int p_which); diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp index 71865f7e8c..956fdc5cfa 100644 --- a/editor/editor_resource_preview.cpp +++ b/editor/editor_resource_preview.cpp @@ -221,7 +221,9 @@ void EditorResourcePreview::_generate_preview(Ref<ImageTexture> &r_texture, Ref< r_small_texture->set_image(small_image); } - break; + if (generated.is_valid()) { + break; + } } if (!p_item.resource.is_valid()) { diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 1056c8abd0..73c17aa760 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -224,6 +224,69 @@ void AnimationPlayerEditor::_autoplay_pressed() { } } +void AnimationPlayerEditor::_go_to_nearest_keyframe(bool p_backward) { + if (_get_current().is_empty()) { + return; + } + + Ref<Animation> anim = player->get_animation(player->get_assigned_animation()); + + double current_time = player->get_current_animation_position(); + // Offset the time to avoid finding the same keyframe with Animation::track_find_key(). + double time_offset = MAX(CMP_EPSILON * 2, current_time * CMP_EPSILON * 2); + double current_time_offset = current_time + (p_backward ? -time_offset : time_offset); + + float nearest_key_time = p_backward ? 0 : anim->get_length(); + int track_count = anim->get_track_count(); + bool bezier_active = track_editor->is_bezier_editor_active(); + + Node *root = get_tree()->get_edited_scene_root(); + EditorSelection *selection = EditorNode::get_singleton()->get_editor_selection(); + + Vector<int> selected_tracks; + for (int i = 0; i < track_count; ++i) { + if (selection->is_selected(root->get_node_or_null(anim->track_get_path(i)))) { + selected_tracks.push_back(i); + } + } + + // Find the nearest keyframe in selection if the scene has selected nodes + // or the nearest keyframe in the entire animation otherwise. + if (selected_tracks.size() > 0) { + for (int track : selected_tracks) { + if (bezier_active && anim->track_get_type(track) != Animation::TYPE_BEZIER) { + continue; + } + int key = anim->track_find_key(track, current_time_offset, Animation::FIND_MODE_NEAREST, false, !p_backward); + if (key == -1) { + continue; + } + double key_time = anim->track_get_key_time(track, key); + if ((p_backward && key_time > nearest_key_time) || (!p_backward && key_time < nearest_key_time)) { + nearest_key_time = key_time; + } + } + } else { + for (int track = 0; track < track_count; ++track) { + if (bezier_active && anim->track_get_type(track) != Animation::TYPE_BEZIER) { + continue; + } + int key = anim->track_find_key(track, current_time_offset, Animation::FIND_MODE_NEAREST, false, !p_backward); + if (key == -1) { + continue; + } + double key_time = anim->track_get_key_time(track, key); + if ((p_backward && key_time > nearest_key_time) || (!p_backward && key_time < nearest_key_time)) { + nearest_key_time = key_time; + } + } + } + + player->seek_internal(nearest_key_time, true, true, true); + frame->set_value(nearest_key_time); + track_editor->set_anim_pos(nearest_key_time); +} + void AnimationPlayerEditor::_play_pressed() { String current = _get_current(); @@ -1511,30 +1574,28 @@ void AnimationPlayerEditor::shortcut_input(const Ref<InputEvent> &p_ev) { ERR_FAIL_COND(p_ev.is_null()); Ref<InputEventKey> k = p_ev; - if (is_visible_in_tree() && k.is_valid() && k->is_pressed() && !k->is_echo() && !k->is_alt_pressed() && !k->is_ctrl_pressed() && !k->is_meta_pressed()) { - switch (k->get_keycode()) { - case Key::A: { - if (!k->is_shift_pressed()) { - _play_bw_from_pressed(); - } else { - _play_bw_pressed(); - } - accept_event(); - } break; - case Key::S: { - _stop_pressed(); - accept_event(); - } break; - case Key::D: { - if (!k->is_shift_pressed()) { - _play_from_pressed(); - } else { - _play_pressed(); - } - accept_event(); - } break; - default: - break; + if (is_visible_in_tree() && k.is_valid() && k->is_pressed() && !k->is_echo()) { + if (ED_IS_SHORTCUT("animation_editor/stop_animation", p_ev)) { + _stop_pressed(); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/play_animation", p_ev)) { + _play_from_pressed(); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/play_animation_backwards", p_ev)) { + _play_bw_from_pressed(); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/play_animation_from_start", p_ev)) { + _play_pressed(); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/play_animation_from_end", p_ev)) { + _play_bw_pressed(); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/go_to_next_keyframe", p_ev)) { + _go_to_nearest_keyframe(false); + accept_event(); + } else if (ED_IS_SHORTCUT("animation_editor/go_to_previous_keyframe", p_ev)) { + _go_to_nearest_keyframe(true); + accept_event(); } } } @@ -1909,27 +1970,27 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug play_bw_from = memnew(Button); play_bw_from->set_theme_type_variation("FlatButton"); - play_bw_from->set_tooltip_text(TTR("Play selected animation backwards from current pos. (A)")); + play_bw_from->set_tooltip_text(TTR("Play Animation Backwards")); hb->add_child(play_bw_from); play_bw = memnew(Button); play_bw->set_theme_type_variation("FlatButton"); - play_bw->set_tooltip_text(TTR("Play selected animation backwards from end. (Shift+A)")); + play_bw->set_tooltip_text(TTR("Play Animation Backwards from End")); hb->add_child(play_bw); stop = memnew(Button); stop->set_theme_type_variation("FlatButton"); + stop->set_tooltip_text(TTR("Pause/Stop Animation")); hb->add_child(stop); - stop->set_tooltip_text(TTR("Pause/stop animation playback. (S)")); play = memnew(Button); play->set_theme_type_variation("FlatButton"); - play->set_tooltip_text(TTR("Play selected animation from start. (Shift+D)")); + play->set_tooltip_text(TTR("Play Animation from Start")); hb->add_child(play); play_from = memnew(Button); play_from->set_theme_type_variation("FlatButton"); - play_from->set_tooltip_text(TTR("Play selected animation from current pos. (D)")); + play_from->set_tooltip_text(TTR("Play Animation")); hb->add_child(play_from); frame = memnew(SpinBox); @@ -2145,6 +2206,14 @@ void fragment() { } )"); RS::get_singleton()->material_set_shader(onion.capture.material->get_rid(), onion.capture.shader->get_rid()); + + ED_SHORTCUT("animation_editor/stop_animation", TTR("Pause/Stop Animation"), Key::S); + ED_SHORTCUT("animation_editor/play_animation", TTR("Play Animation"), Key::D); + ED_SHORTCUT("animation_editor/play_animation_backwards", TTR("Play Animation Backwards"), Key::A); + ED_SHORTCUT("animation_editor/play_animation_from_start", TTR("Play Animation from Start"), KeyModifierMask::SHIFT + Key::D); + ED_SHORTCUT("animation_editor/play_animation_from_end", TTR("Play Animation Backwards from End"), KeyModifierMask::SHIFT + Key::A); + ED_SHORTCUT("animation_editor/go_to_next_keyframe", TTR("Go to Next Keyframe"), KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::D); + ED_SHORTCUT("animation_editor/go_to_previous_keyframe", TTR("Go to Previous Keyframe"), KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::A); } AnimationPlayerEditor::~AnimationPlayerEditor() { diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 8804e4e3b4..860d421b91 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -178,6 +178,7 @@ class AnimationPlayerEditor : public VBoxContainer { void _select_anim_by_name(const String &p_anim); float _get_editor_step() const; + void _go_to_nearest_keyframe(bool p_backward); void _play_pressed(); void _play_from_pressed(); void _play_bw_pressed(); diff --git a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp index ae24b4250e..c277ec8cd3 100644 --- a/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/joint_3d_gizmo_plugin.cpp @@ -293,9 +293,15 @@ Joint3DGizmoPlugin::Joint3DGizmoPlugin() { void Joint3DGizmoPlugin::incremental_update_gizmos() { if (!current_gizmos.is_empty()) { - update_idx++; - update_idx = update_idx % current_gizmos.size(); - redraw(current_gizmos.get(update_idx)); + HashSet<EditorNode3DGizmo *>::Iterator E = current_gizmos.find(last_drawn); + if (E) { + ++E; + } + if (!E) { + E = current_gizmos.begin(); + } + redraw(*E); + last_drawn = *E; } } diff --git a/editor/plugins/gizmos/joint_3d_gizmo_plugin.h b/editor/plugins/gizmos/joint_3d_gizmo_plugin.h index 79fe40d1b2..25b08d71c6 100644 --- a/editor/plugins/gizmos/joint_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/joint_3d_gizmo_plugin.h @@ -37,7 +37,7 @@ class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin); Timer *update_timer = nullptr; - uint64_t update_idx = 0; + EditorNode3DGizmo *last_drawn = nullptr; void incremental_update_gizmos(); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 67d5e44ce5..1b2cd441ad 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -1060,7 +1060,7 @@ Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { ref->set_node_3d(p_spatial); ref->set_hidden(current_state == HIDDEN); - current_gizmos.push_back(ref.ptr()); + current_gizmos.insert(ref.ptr()); return ref; } diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index c4b275032a..1916bc2058 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -157,7 +157,7 @@ public: protected: int current_state; - List<EditorNode3DGizmo *> current_gizmos; + HashSet<EditorNode3DGizmo *> current_gizmos; HashMap<String, Vector<Ref<StandardMaterial3D>>> materials; static void _bind_methods(); diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp index ae336d6f9d..59b669eabb 100644 --- a/platform/android/file_access_android.cpp +++ b/platform/android/file_access_android.cpp @@ -113,87 +113,6 @@ bool FileAccessAndroid::eof_reached() const { return eof; } -uint8_t FileAccessAndroid::get_8() const { - if (pos >= len) { - eof = true; - return 0; - } - - uint8_t byte; - AAsset_read(asset, &byte, 1); - pos++; - return byte; -} - -uint16_t FileAccessAndroid::get_16() const { - if (pos >= len) { - eof = true; - return 0; - } - - uint16_t bytes = 0; - int r = AAsset_read(asset, &bytes, 2); - - if (r >= 0) { - pos += r; - if (pos >= len) { - eof = true; - } - } - - if (big_endian) { - bytes = BSWAP16(bytes); - } - - return bytes; -} - -uint32_t FileAccessAndroid::get_32() const { - if (pos >= len) { - eof = true; - return 0; - } - - uint32_t bytes = 0; - int r = AAsset_read(asset, &bytes, 4); - - if (r >= 0) { - pos += r; - if (pos >= len) { - eof = true; - } - } - - if (big_endian) { - bytes = BSWAP32(bytes); - } - - return bytes; -} - -uint64_t FileAccessAndroid::get_64() const { - if (pos >= len) { - eof = true; - return 0; - } - - uint64_t bytes = 0; - int r = AAsset_read(asset, &bytes, 8); - - if (r >= 0) { - pos += r; - if (pos >= len) { - eof = true; - } - } - - if (big_endian) { - bytes = BSWAP64(bytes); - } - - return bytes; -} - uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); @@ -209,6 +128,7 @@ uint64_t FileAccessAndroid::get_buffer(uint8_t *p_dst, uint64_t p_length) const pos = len; } } + return r; } @@ -220,19 +140,7 @@ void FileAccessAndroid::flush() { ERR_FAIL(); } -void FileAccessAndroid::store_8(uint8_t p_dest) { - ERR_FAIL(); -} - -void FileAccessAndroid::store_16(uint16_t p_dest) { - ERR_FAIL(); -} - -void FileAccessAndroid::store_32(uint32_t p_dest) { - ERR_FAIL(); -} - -void FileAccessAndroid::store_64(uint64_t p_dest) { +void FileAccessAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL(); } diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h index b465a92c78..3224ab50b9 100644 --- a/platform/android/file_access_android.h +++ b/platform/android/file_access_android.h @@ -68,19 +68,12 @@ public: virtual bool eof_reached() const override; // reading passed EOF virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } - virtual uint8_t get_8() const override; // get a byte - virtual uint16_t get_16() const override; - virtual uint32_t get_32() const override; - virtual uint64_t get_64() const override; virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; // get last error virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; // store a byte - virtual void store_16(uint16_t p_dest) override; - virtual void store_32(uint32_t p_dest) override; - virtual void store_64(uint64_t p_dest) override; + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_path) override; // return true if a file exists diff --git a/platform/android/file_access_filesystem_jandroid.cpp b/platform/android/file_access_filesystem_jandroid.cpp index 9ae48dfb10..8b52a00ed8 100644 --- a/platform/android/file_access_filesystem_jandroid.cpp +++ b/platform/android/file_access_filesystem_jandroid.cpp @@ -169,43 +169,6 @@ void FileAccessFilesystemJAndroid::_set_eof(bool eof) { } } -uint8_t FileAccessFilesystemJAndroid::get_8() const { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); - uint8_t byte; - get_buffer(&byte, 1); - return byte; -} - -uint16_t FileAccessFilesystemJAndroid::get_16() const { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); - uint16_t bytes = 0; - get_buffer(reinterpret_cast<uint8_t *>(&bytes), 2); - if (big_endian) { - bytes = BSWAP16(bytes); - } - return bytes; -} - -uint32_t FileAccessFilesystemJAndroid::get_32() const { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); - uint32_t bytes = 0; - get_buffer(reinterpret_cast<uint8_t *>(&bytes), 4); - if (big_endian) { - bytes = BSWAP32(bytes); - } - return bytes; -} - -uint64_t FileAccessFilesystemJAndroid::get_64() const { - ERR_FAIL_COND_V_MSG(!is_open(), 0, "File must be opened before use."); - uint64_t bytes = 0; - get_buffer(reinterpret_cast<uint8_t *>(&bytes), 8); - if (big_endian) { - bytes = BSWAP64(bytes); - } - return bytes; -} - String FileAccessFilesystemJAndroid::get_line() const { ERR_FAIL_COND_V_MSG(!is_open(), String(), "File must be opened before use."); @@ -271,31 +234,6 @@ uint64_t FileAccessFilesystemJAndroid::get_buffer(uint8_t *p_dst, uint64_t p_len } } -void FileAccessFilesystemJAndroid::store_8(uint8_t p_dest) { - store_buffer(&p_dest, 1); -} - -void FileAccessFilesystemJAndroid::store_16(uint16_t p_dest) { - if (big_endian) { - p_dest = BSWAP16(p_dest); - } - store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 2); -} - -void FileAccessFilesystemJAndroid::store_32(uint32_t p_dest) { - if (big_endian) { - p_dest = BSWAP32(p_dest); - } - store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 4); -} - -void FileAccessFilesystemJAndroid::store_64(uint64_t p_dest) { - if (big_endian) { - p_dest = BSWAP64(p_dest); - } - store_buffer(reinterpret_cast<uint8_t *>(&p_dest), 8); -} - void FileAccessFilesystemJAndroid::store_buffer(const uint8_t *p_src, uint64_t p_length) { if (_file_write) { ERR_FAIL_COND_MSG(!is_open(), "File must be opened before use."); diff --git a/platform/android/file_access_filesystem_jandroid.h b/platform/android/file_access_filesystem_jandroid.h index 2795ac02ac..1345b72fa6 100644 --- a/platform/android/file_access_filesystem_jandroid.h +++ b/platform/android/file_access_filesystem_jandroid.h @@ -78,20 +78,12 @@ public: virtual bool eof_reached() const override; ///< reading passed EOF virtual Error resize(int64_t p_length) override; - virtual uint8_t get_8() const override; ///< get a byte - virtual uint16_t get_16() const override; - virtual uint32_t get_32() const override; - virtual uint64_t get_64() const override; virtual String get_line() const override; ///< get a line virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; virtual Error get_error() const override; ///< get last error virtual void flush() override; - virtual void store_8(uint8_t p_dest) override; ///< store a byte - virtual void store_16(uint16_t p_dest) override; - virtual void store_32(uint32_t p_dest) override; - virtual void store_64(uint64_t p_dest) override; virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; virtual bool file_exists(const String &p_path) override; ///< return true if a file exists diff --git a/platform/windows/detect.py b/platform/windows/detect.py index d2a9c2315f..92ac921cee 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -649,6 +649,61 @@ def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config): env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)]) +def get_ar_version(env): + ret = { + "major": -1, + "minor": -1, + "patch": -1, + "is_llvm": False, + } + try: + output = ( + subprocess.check_output([env.subst(env["AR"]), "--version"], shell=(os.name == "nt")) + .strip() + .decode("utf-8") + ) + except (subprocess.CalledProcessError, OSError): + print_warning("Couldn't check version of `ar`.") + return ret + + match = re.search(r"GNU ar \(GNU Binutils\) (\d+)\.(\d+)(:?\.(\d+))?", output) + if match: + ret["major"] = int(match[1]) + ret["minor"] = int(match[2]) + if match[3]: + ret["patch"] = int(match[3]) + else: + ret["patch"] = 0 + return ret + + match = re.search(r"LLVM version (\d+)\.(\d+)\.(\d+)", output) + if match: + ret["major"] = int(match[1]) + ret["minor"] = int(match[2]) + ret["patch"] = int(match[3]) + ret["is_llvm"] = True + return ret + + print_warning("Couldn't parse version of `ar`.") + return ret + + +def get_is_ar_thin_supported(env): + """Check whether `ar --thin` is supported. It is only supported since Binutils 2.38 or LLVM 14.""" + ar_version = get_ar_version(env) + if ar_version["major"] == -1: + return False + + if ar_version["is_llvm"]: + return ar_version["major"] >= 14 + + if ar_version["major"] == 2: + return ar_version["minor"] >= 38 + + print_warning("Unknown Binutils `ar` version.") + return False + + def configure_mingw(env: "SConsEnvironment"): # Workaround for MinGW. See: # https://www.scons.org/wiki/LongCmdLinesOnWin32 @@ -781,7 +836,8 @@ def configure_mingw(env: "SConsEnvironment"): if env["use_llvm"] and os.name == "nt" and methods._colorize: env.Append(CCFLAGS=["$(-fansi-escape-codes$)", "$(-fcolor-diagnostics$)"]) - env.Append(ARFLAGS=["--thin"]) + if get_is_ar_thin_supported(env): + env.Append(ARFLAGS=["--thin"]) env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"]) env.Append( diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index 20dd12f8c3..0ab4dbe470 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -947,14 +947,6 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) { clear_animation_instances(); } -Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) { - Variant res; - if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) { - return res; - } - return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx); -} - Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) { #ifndef _3D_DISABLED switch (p_anim->track_get_type(p_track)) { @@ -974,6 +966,17 @@ Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, in return p_value; } +Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) { + if (is_GDVIRTUAL_CALL_post_process_key_value) { + Variant res; + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) { + return res; + } + is_GDVIRTUAL_CALL_post_process_key_value = false; + } + return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx); +} + void AnimationMixer::_blend_init() { // Check all tracks, see if they need modification. root_motion_position = Vector3(0, 0, 0); @@ -1080,22 +1083,26 @@ void AnimationMixer::_blend_calc_total_weight() { for (const AnimationInstance &ai : animation_instances) { Ref<Animation> a = ai.animation_data.animation; real_t weight = ai.playback_info.weight; - Vector<real_t> track_weights = ai.playback_info.track_weights; - Vector<Animation::TypeHash> processed_hashes; - for (int i = 0; i < a->get_track_count(); i++) { - if (!a->track_is_enabled(i)) { + const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr(); + int track_weights_count = ai.playback_info.track_weights.size(); + static LocalVector<Animation::TypeHash> processed_hashes; + processed_hashes.clear(); + const Vector<Animation::Track *> tracks = a->get_tracks(); + for (const Animation::Track *animation_track : tracks) { + if (!animation_track->enabled) { continue; } - Animation::TypeHash thash = a->track_get_type_hash(i); - if (!track_cache.has(thash) || processed_hashes.has(thash)) { + Animation::TypeHash thash = animation_track->thash; + TrackCache **track_ptr = track_cache.getptr(thash); + if (track_ptr == nullptr || processed_hashes.has(thash)) { // No path, but avoid error spamming. // Or, there is the case different track type with same path; These can be distinguished by hash. So don't add the weight doubly. continue; } - TrackCache *track = track_cache[thash]; + TrackCache *track = *track_ptr; int blend_idx = track_map[track->path]; ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count); - real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight; + real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight; track->total_weight += blend; processed_hashes.push_back(thash); } @@ -1115,26 +1122,33 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { Animation::LoopedFlag looped_flag = ai.playback_info.looped_flag; bool is_external_seeking = ai.playback_info.is_external_seeking; real_t weight = ai.playback_info.weight; - Vector<real_t> track_weights = ai.playback_info.track_weights; + const real_t *track_weights_ptr = ai.playback_info.track_weights.ptr(); + int track_weights_count = ai.playback_info.track_weights.size(); bool backward = signbit(delta); // This flag is used by the root motion calculates or detecting the end of audio stream. bool seeked_backward = signbit(p_delta); #ifndef _3D_DISABLED bool calc_root = !seeked || is_external_seeking; #endif // _3D_DISABLED - - for (int i = 0; i < a->get_track_count(); i++) { - if (!a->track_is_enabled(i)) { + const Vector<Animation::Track *> tracks = a->get_tracks(); + Animation::Track *const *tracks_ptr = tracks.ptr(); + real_t a_length = a->get_length(); + int count = tracks.size(); + for (int i = 0; i < count; i++) { + const Animation::Track *animation_track = tracks_ptr[i]; + if (!animation_track->enabled) { continue; } - Animation::TypeHash thash = a->track_get_type_hash(i); - if (!track_cache.has(thash)) { + Animation::TypeHash thash = animation_track->thash; + TrackCache **track_ptr = track_cache.getptr(thash); + if (track_ptr == nullptr) { continue; // No path, but avoid error spamming. } - TrackCache *track = track_cache[thash]; - ERR_CONTINUE(!track_map.has(track->path)); - int blend_idx = track_map[track->path]; + TrackCache *track = *track_ptr; + int *blend_idx_ptr = track_map.getptr(track->path); + ERR_CONTINUE(blend_idx_ptr == nullptr); + int blend_idx = *blend_idx_ptr; ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count); - real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight; + real_t blend = blend_idx < track_weights_count ? track_weights_ptr[blend_idx] * weight : weight; if (!deterministic) { // If non-deterministic, do normalization. // It would be better to make this if statement outside the for loop, but come here since too much code... @@ -1143,8 +1157,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } blend = blend / track->total_weight; } - Animation::TrackType ttype = a->track_get_type(i); - track->root_motion = root_motion_track == a->track_get_path(i); + Animation::TrackType ttype = animation_track->type; + track->root_motion = root_motion_track == animation_track->path; switch (ttype) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED @@ -1161,26 +1175,26 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { prev_time = 0; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; } } } else { - if (Animation::is_greater_approx(prev_time, (double)a->get_length())) { + if (Animation::is_greater_approx(prev_time, (double)a_length)) { switch (a->get_loop_mode()) { case Animation::LOOP_NONE: { - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; @@ -1195,7 +1209,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { continue; } loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); - a->try_position_track_interpolate(i, (double)a->get_length(), &loc[1]); + a->try_position_track_interpolate(i, (double)a_length, &loc[1]); loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; prev_time = 0; @@ -1210,7 +1224,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { a->try_position_track_interpolate(i, 0, &loc[1]); loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } } Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]); @@ -1221,7 +1235,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { a->try_position_track_interpolate(i, time, &loc[1]); loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; - prev_time = !backward ? 0 : (double)a->get_length(); + prev_time = !backward ? 0 : (double)a_length; } { Vector3 loc; @@ -1249,26 +1263,26 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { prev_time = 0; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; } } } else { - if (Animation::is_greater_approx(prev_time, (double)a->get_length())) { + if (Animation::is_greater_approx(prev_time, (double)a_length)) { switch (a->get_loop_mode()) { case Animation::LOOP_NONE: { - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; @@ -1283,7 +1297,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { continue; } rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); - a->try_rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); + a->try_rotation_track_interpolate(i, (double)a_length, &rot[1]); rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; @@ -1297,7 +1311,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); a->try_rotation_track_interpolate(i, 0, &rot[1]); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } } Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]); @@ -1308,7 +1322,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { a->try_rotation_track_interpolate(i, time, &rot[1]); rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); - prev_time = !backward ? 0 : (double)a->get_length(); + prev_time = !backward ? 0 : (double)a_length; } { Quaternion rot; @@ -1336,26 +1350,26 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { prev_time = 0; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; } } } else { - if (Animation::is_greater_approx(prev_time, (double)a->get_length())) { + if (Animation::is_greater_approx(prev_time, (double)a_length)) { switch (a->get_loop_mode()) { case Animation::LOOP_NONE: { - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } break; case Animation::LOOP_LINEAR: { - prev_time = Math::fposmod(prev_time, (double)a->get_length()); + prev_time = Math::fposmod(prev_time, (double)a_length); } break; case Animation::LOOP_PINGPONG: { - prev_time = Math::pingpong(prev_time, (double)a->get_length()); + prev_time = Math::pingpong(prev_time, (double)a_length); } break; default: break; @@ -1370,7 +1384,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { continue; } scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx); - a->try_scale_track_interpolate(i, (double)a->get_length(), &scale[1]); + a->try_scale_track_interpolate(i, (double)a_length, &scale[1]); root_motion_cache.scale += (scale[1] - scale[0]) * blend; scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); prev_time = 0; @@ -1385,7 +1399,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { a->try_scale_track_interpolate(i, 0, &scale[1]); scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); root_motion_cache.scale += (scale[1] - scale[0]) * blend; - prev_time = (double)a->get_length(); + prev_time = (double)a_length; } } Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]); @@ -1396,7 +1410,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { a->try_scale_track_interpolate(i, time, &scale[1]); scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); root_motion_cache.scale += (scale[1] - scale[0]) * blend; - prev_time = !backward ? 0 : (double)a->get_length(); + prev_time = !backward ? 0 : (double)a_length; } { Vector3 scale; @@ -1560,7 +1574,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } PlayingAudioTrackInfo &track_info = t->playing_streams[oid]; - track_info.length = a->get_length(); + track_info.length = a_length; track_info.time = time; track_info.volume += blend; track_info.loop = a->get_loop_mode() != Animation::LOOP_NONE; @@ -1679,7 +1693,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); // Seek to loop. } break; case Animation::LOOP_PINGPONG: { - at_anim_pos = Math::pingpong(time - pos, (double)a->get_length()); + at_anim_pos = Math::pingpong(time - pos, (double)a_length); } break; default: break; @@ -1717,6 +1731,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } } } + is_GDVIRTUAL_CALL_post_process_key_value = true; } void AnimationMixer::_blend_apply() { diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index c029c68ae1..5482197fbd 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -48,6 +48,7 @@ class AnimationMixer : public Node { #endif // TOOLS_ENABLED bool reset_on_save = true; + bool is_GDVIRTUAL_CALL_post_process_key_value = true; public: enum AnimationCallbackModeProcess { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index c5a6a99d07..867bbda4b3 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -203,10 +203,11 @@ AnimationNode::NodeTimeInfo AnimationNode::_blend_node(Ref<AnimationNode> p_node } for (const KeyValue<NodePath, bool> &E : filter) { - if (!process_state->track_map.has(E.key)) { + const HashMap<NodePath, int> &map = *process_state->track_map; + if (!map.has(E.key)) { continue; } - int idx = process_state->track_map[E.key]; + int idx = map[E.key]; blendw[idx] = 1.0; // Filtered goes to one. } @@ -618,7 +619,7 @@ bool AnimationTree::_blend_pre_process(double p_delta, int p_track_count, const process_state.valid = true; process_state.invalid_reasons = ""; process_state.last_pass = process_pass; - process_state.track_map = p_track_map; + process_state.track_map = &p_track_map; // Init node state for root AnimationNode. root_animation_node->node_state.track_weights.resize(p_track_count); diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index aa497ff1d6..d4b7bf31c9 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -106,7 +106,7 @@ public: // Temporary state for blending process which needs to be started in the AnimationTree, pass through the AnimationNodes, and then return to the AnimationTree. struct ProcessState { AnimationTree *tree = nullptr; - HashMap<NodePath, int> track_map; // TODO: Is there a better way to manage filter/tracks? + const HashMap<NodePath, int> *track_map; // TODO: Is there a better way to manage filter/tracks? bool is_testing = false; bool valid = false; String invalid_reasons; diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 9e6d34959a..0c29790ea4 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -45,7 +45,7 @@ public: static inline String PARAMETERS_BASE_PATH = "parameters/"; - enum TrackType { + enum TrackType : uint8_t { TYPE_VALUE, // Set a value in a property, can be interpolated. TYPE_POSITION_3D, // Position 3D track, can be compressed. TYPE_ROTATION_3D, // Rotation 3D track, can be compressed. @@ -57,7 +57,7 @@ public: TYPE_ANIMATION, }; - enum InterpolationType { + enum InterpolationType : uint8_t { INTERPOLATION_NEAREST, INTERPOLATION_LINEAR, INTERPOLATION_CUBIC, @@ -65,26 +65,26 @@ public: INTERPOLATION_CUBIC_ANGLE, }; - enum UpdateMode { + enum UpdateMode : uint8_t { UPDATE_CONTINUOUS, UPDATE_DISCRETE, UPDATE_CAPTURE, }; - enum LoopMode { + enum LoopMode : uint8_t { LOOP_NONE, LOOP_LINEAR, LOOP_PINGPONG, }; // LoopedFlag is used in Animataion to "process the keys at both ends correct". - enum LoopedFlag { + enum LoopedFlag : uint8_t { LOOPED_FLAG_NONE, LOOPED_FLAG_END, LOOPED_FLAG_START, }; - enum FindMode { + enum FindMode : uint8_t { FIND_MODE_NEAREST, FIND_MODE_APPROX, FIND_MODE_EXACT, @@ -104,7 +104,6 @@ public: }; #endif // TOOLS_ENABLED -private: struct Track { TrackType type = TrackType::TYPE_ANIMATION; InterpolationType interpolation = INTERPOLATION_LINEAR; @@ -117,6 +116,7 @@ private: virtual ~Track() {} }; +private: struct Key { real_t transition = 1.0; double time = 0.0; // Time in secs. @@ -396,6 +396,10 @@ public: int add_track(TrackType p_type, int p_at_pos = -1); void remove_track(int p_track); + _FORCE_INLINE_ const Vector<Track *> get_tracks() { + return tracks; + } + bool is_capture_included() const; int get_track_count() const; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 6e5e8f63e0..be29716f45 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1470,8 +1470,24 @@ void TextureStorage::texture_debug_usage(List<RS::TextureInfo> *r_info) { tinfo.format = t->format; tinfo.width = t->width; tinfo.height = t->height; - tinfo.depth = t->depth; - tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps); + tinfo.bytes = Image::get_image_data_size(t->width, t->height, t->format, t->mipmaps > 1); + + switch (t->type) { + case TextureType::TYPE_3D: + tinfo.depth = t->depth; + tinfo.bytes *= t->depth; + break; + + case TextureType::TYPE_LAYERED: + tinfo.depth = t->layers; + tinfo.bytes *= t->layers; + break; + + default: + tinfo.depth = 0; + break; + } + r_info->push_back(tinfo); } } diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 92f0f0dbc0..1848d5602e 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -3263,6 +3263,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_set_z_index", "item", "z_index"), &RenderingServer::canvas_item_set_z_index); ClassDB::bind_method(D_METHOD("canvas_item_set_z_as_relative_to_parent", "item", "enabled"), &RenderingServer::canvas_item_set_z_as_relative_to_parent); ClassDB::bind_method(D_METHOD("canvas_item_set_copy_to_backbuffer", "item", "enabled", "rect"), &RenderingServer::canvas_item_set_copy_to_backbuffer); + ClassDB::bind_method(D_METHOD("canvas_item_attach_skeleton", "item", "skeleton"), &RenderingServer::canvas_item_attach_skeleton); ClassDB::bind_method(D_METHOD("canvas_item_clear", "item"), &RenderingServer::canvas_item_clear); ClassDB::bind_method(D_METHOD("canvas_item_set_draw_index", "item", "index"), &RenderingServer::canvas_item_set_draw_index); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index d8b6651833..62ca6b3b6d 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -176,7 +176,7 @@ public: uint32_t height; uint32_t depth; Image::Format format; - int bytes; + int64_t bytes; String path; }; diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 67ebc9381e..02fec07448 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -15,5 +15,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.14.7" +#define THORVG_VERSION_STRING "0.14.8" #endif diff --git a/thirdparty/thorvg/patches/pr2702-sw_engine-handle-small-cubics.patch b/thirdparty/thorvg/patches/pr2702-sw_engine-handle-small-cubics.patch new file mode 100644 index 0000000000..69f4a5cf85 --- /dev/null +++ b/thirdparty/thorvg/patches/pr2702-sw_engine-handle-small-cubics.patch @@ -0,0 +1,96 @@ +From ac7d208ed8e4651c93ce1b2384070fccac9b6cb6 Mon Sep 17 00:00:00 2001 +From: Mira Grudzinska <mira@lottiefiles.com> +Date: Sun, 1 Sep 2024 22:36:18 +0200 +Subject: [PATCH] sw_engine: handle small cubics + +During the stroke's outline calculation, the function +handling small cubics set all angles to zero. Such cases +should be ignored, as further processing caused errors - +when the cubic was small but not zero, setting the angles +to zero resulted in incorrect outlines. + +@Issue: https://github.com/godotengine/godot/issues/96262 +--- + src/renderer/sw_engine/tvgSwCommon.h | 3 ++- + src/renderer/sw_engine/tvgSwMath.cpp | 19 ++++++++++++------- + src/renderer/sw_engine/tvgSwStroke.cpp | 16 +++++++++++----- + 3 files changed, 25 insertions(+), 13 deletions(-) + +diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h +index 893e9beca..158fe8ecd 100644 +--- a/src/renderer/sw_engine/tvgSwCommon.h ++++ b/src/renderer/sw_engine/tvgSwCommon.h +@@ -491,7 +491,8 @@ SwFixed mathSin(SwFixed angle); + void mathSplitCubic(SwPoint* base); + SwFixed mathDiff(SwFixed angle1, SwFixed angle2); + SwFixed mathLength(const SwPoint& pt); +-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); ++bool mathSmallCubic(const SwPoint* base); ++bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); + SwFixed mathMean(SwFixed angle1, SwFixed angle2); + SwPoint mathTransform(const Point* to, const Matrix& transform); + bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); +diff --git a/src/renderer/sw_engine/tvgSwMath.cpp b/src/renderer/sw_engine/tvgSwMath.cpp +index 1093edd62..b311be05f 100644 +--- a/src/renderer/sw_engine/tvgSwMath.cpp ++++ b/src/renderer/sw_engine/tvgSwMath.cpp +@@ -44,7 +44,17 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2) + } + + +-bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) ++bool mathSmallCubic(const SwPoint* base) ++{ ++ auto d1 = base[2] - base[3]; ++ auto d2 = base[1] - base[2]; ++ auto d3 = base[0] - base[1]; ++ ++ return d1.small() && d2.small() && d3.small(); ++} ++ ++ ++bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) + { + auto d1 = base[2] - base[3]; + auto d2 = base[1] - base[2]; +@@ -52,12 +62,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw + + if (d1.small()) { + if (d2.small()) { +- if (d3.small()) { +- angleIn = angleMid = angleOut = 0; +- return true; +- } else { +- angleIn = angleMid = angleOut = mathAtan(d3); +- } ++ angleIn = angleMid = angleOut = mathAtan(d3); + } else { + if (d3.small()) { + angleIn = angleMid = angleOut = mathAtan(d2); +diff --git a/src/renderer/sw_engine/tvgSwStroke.cpp b/src/renderer/sw_engine/tvgSwStroke.cpp +index 575d12951..4679b72cc 100644 +--- a/src/renderer/sw_engine/tvgSwStroke.cpp ++++ b/src/renderer/sw_engine/tvgSwStroke.cpp +@@ -441,11 +441,17 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl + //initialize with current direction + angleIn = angleOut = angleMid = stroke.angleIn; + +- if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) { +- if (stroke.firstPt) stroke.angleIn = angleIn; +- mathSplitCubic(arc); +- arc += 3; +- continue; ++ if (arc < limit) { ++ if (mathSmallCubic(arc)) { ++ arc -= 3; ++ continue; ++ } ++ if (!mathFlatCubic(arc, angleIn, angleMid, angleOut)) { ++ if (stroke.firstPt) stroke.angleIn = angleIn; ++ mathSplitCubic(arc); ++ arc += 3; ++ continue; ++ } + } + + if (firstArc) { diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp index c56b32249f..0254cce9b8 100644 --- a/thirdparty/thorvg/src/common/tvgMath.cpp +++ b/thirdparty/thorvg/src/common/tvgMath.cpp @@ -133,3 +133,11 @@ Point operator*(const Point& pt, const Matrix& m) auto ty = pt.x * m.e21 + pt.y * m.e22 + m.e23; return {tx, ty}; } + +uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t) +{ + auto result = static_cast<int>(start + (end - start) * t); + if (result > 255) result = 255; + else if (result < 0) result = 0; + return static_cast<uint8_t>(result); +} diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h index 556ed410ff..df39e3b9af 100644 --- a/thirdparty/thorvg/src/common/tvgMath.h +++ b/thirdparty/thorvg/src/common/tvgMath.h @@ -259,5 +259,6 @@ static inline T mathLerp(const T &start, const T &end, float t) return static_cast<T>(start + (end - start) * t); } +uint8_t mathLerp(const uint8_t &start, const uint8_t &end, float t); #endif //_TVG_MATH_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index 197d3cc13a..5aab4f1b0d 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -588,6 +588,11 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re float _red = 0, _green = 0, _blue = 0; uint32_t i = 0; + while (hue < 0) hue += 360.0f; + hue = fmod(hue, 360.0f); + saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f; + brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f; + if (mathZero(saturation)) _red = _green = _blue = brightness; else { if (mathEqual(hue, 360.0)) hue = 0.0f; @@ -714,7 +719,6 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** const char* hue = nullptr; if (_parseNumber(&content, &hue, &th) && hue) { const char* saturation = nullptr; - th = float(uint32_t(th) % 360); hue = _skipSpace(hue, nullptr); hue = (char*)_skipComma(hue); hue = _skipSpace(hue, nullptr); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 3229eb39ba..09b75d370b 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -367,7 +367,7 @@ static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNU } //TODO: BlendMethod could remove the alpha parameter. -static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, uint8_t a) { //if (s > d) => s - d //else => d - s @@ -404,8 +404,7 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t return JOIN(255, c1, c2, c3); } - -static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) +static inline uint32_t opBlendDirectMultiply(uint32_t s, uint32_t d, uint8_t a) { // s * d auto c1 = MULTIPLY(C1(s), C1(d)); @@ -414,6 +413,10 @@ static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_ return JOIN(255, c1, c2, c3); } +static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, uint8_t a) +{ + return opBlendDirectMultiply(s, d, a) + ALPHA_BLEND(d, IA(s)); +} static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a) { @@ -492,7 +495,8 @@ SwFixed mathSin(SwFixed angle); void mathSplitCubic(SwPoint* base); SwFixed mathDiff(SwFixed angle1, SwFixed angle2); SwFixed mathLength(const SwPoint& pt); -bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); +bool mathSmallCubic(const SwPoint* base); +bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwPoint mathTransform(const Point* to, const Matrix& transform); bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp index fb809c4f7e..60dbbc4fbc 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp @@ -44,7 +44,17 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2) } -bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) +bool mathSmallCubic(const SwPoint* base) +{ + auto d1 = base[2] - base[3]; + auto d2 = base[1] - base[2]; + auto d3 = base[0] - base[1]; + + return d1.small() && d2.small() && d3.small(); +} + + +bool mathFlatCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut) { auto d1 = base[2] - base[3]; auto d2 = base[1] - base[2]; @@ -52,12 +62,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw if (d1.small()) { if (d2.small()) { - if (d3.small()) { - angleIn = angleMid = angleOut = 0; - return true; - } else { - angleIn = angleMid = angleOut = mathAtan(d3); - } + angleIn = angleMid = angleOut = mathAtan(d3); } else { if (d3.small()) { angleIn = angleMid = angleOut = mathAtan(d2); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index a6f9d8745a..90d91e17e0 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -1383,7 +1383,7 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, *dst = INTERPOLATE(tmp, *dst, A(*src)); } } else { - for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) { + for (auto x = region.min.x; x < region.max.x; x++, dst++, src++) { auto tmp = ALPHA_BLEND(*src, opacity); auto tmp2 = surface->blender(tmp, *dst, 255); *dst = INTERPOLATE(tmp2, *dst, A(tmp)); diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 47a7166115..6470089c7c 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -100,7 +100,6 @@ struct SwShapeTask : SwTask return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12)); } - bool clip(SwRleData* target) override { if (shape.fastTrack) rleClipRect(target, &bbox); @@ -447,7 +446,7 @@ bool SwRenderer::renderShape(RenderData data) } -bool SwRenderer::blend(BlendMethod method) +bool SwRenderer::blend(BlendMethod method, bool direct) { if (surface->blendMethod == method) return true; surface->blendMethod = method; @@ -460,7 +459,7 @@ bool SwRenderer::blend(BlendMethod method) surface->blender = opBlendScreen; break; case BlendMethod::Multiply: - surface->blender = opBlendMultiply; + surface->blender = direct ? opBlendDirectMultiply : opBlendMultiply; break; case BlendMethod::Overlay: surface->blender = opBlendOverlay; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index fcd8ad4620..7aa6d89aaa 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -46,7 +46,7 @@ public: RenderRegion region(RenderData data) override; RenderRegion viewport() override; bool viewport(const RenderRegion& vp) override; - bool blend(BlendMethod method) override; + bool blend(BlendMethod method, bool direct) override; ColorSpace colorSpace() override; const Surface* mainSurface() override; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp index 75ac96be04..e0e74ce53c 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp @@ -441,11 +441,17 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl //initialize with current direction angleIn = angleOut = angleMid = stroke.angleIn; - if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) { - if (stroke.firstPt) stroke.angleIn = angleIn; - mathSplitCubic(arc); - arc += 3; - continue; + if (arc < limit) { + if (mathSmallCubic(arc)) { + arc -= 3; + continue; + } + if (!mathFlatCubic(arc, angleIn, angleMid, angleOut)) { + if (stroke.firstPt) stroke.angleIn = angleIn; + mathSplitCubic(arc); + arc += 3; + continue; + } } if (firstArc) { diff --git a/thirdparty/thorvg/src/renderer/tvgLoadModule.h b/thirdparty/thorvg/src/renderer/tvgLoadModule.h index c750683771..1b81d81a4f 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoadModule.h +++ b/thirdparty/thorvg/src/renderer/tvgLoadModule.h @@ -101,7 +101,8 @@ struct FontLoader : LoadModule FontLoader(FileType type) : LoadModule(type) {} - virtual bool request(Shape* shape, char* text, bool italic = false) = 0; + virtual bool request(Shape* shape, char* text) = 0; + virtual bool transform(Paint* paint, float fontSize, bool italic) = 0; }; #endif //_TVG_LOAD_MODULE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 11cee0c54a..37813b19ef 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -225,8 +225,6 @@ bool Paint::Impl::render(RenderMethod* renderer) if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity); - renderer->blend(blendMethod); - bool ret; PAINT_METHOD(ret, render(renderer)); diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp index 68c7a41032..e6653b1c99 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPicture.cpp @@ -73,6 +73,8 @@ bool Picture::Impl::needComposition(uint8_t opacity) bool Picture::Impl::render(RenderMethod* renderer) { bool ret = false; + renderer->blend(picture->blend(), true); + if (surface) return renderer->renderImage(rd); else if (paint) { Compositor* cmp = nullptr; diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index b0ee42db8d..f1042884ec 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/thirdparty/thorvg/src/renderer/tvgRender.h @@ -296,7 +296,7 @@ public: virtual RenderRegion region(RenderData data) = 0; virtual RenderRegion viewport() = 0; virtual bool viewport(const RenderRegion& vp) = 0; - virtual bool blend(BlendMethod method) = 0; + virtual bool blend(BlendMethod method, bool direct = false) = 0; virtual ColorSpace colorSpace() = 0; virtual const Surface* mainSurface() = 0; diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h index cb6d179326..190ecd31b9 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.h +++ b/thirdparty/thorvg/src/renderer/tvgScene.h @@ -120,6 +120,8 @@ struct Scene::Impl Compositor* cmp = nullptr; auto ret = true; + renderer->blend(scene->blend()); + if (needComp) { cmp = renderer->target(bounds(renderer), renderer->colorSpace()); renderer->beginComposite(cmp, CompositeMethod::None, opacity); diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h index b76fde7ced..94704aee67 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/thirdparty/thorvg/src/renderer/tvgShape.h @@ -54,10 +54,13 @@ struct Shape::Impl Compositor* cmp = nullptr; bool ret; + renderer->blend(shape->blend(), !needComp); + if (needComp) { cmp = renderer->target(bounds(renderer), renderer->colorSpace()); renderer->beginComposite(cmp, CompositeMethod::None, opacity); } + ret = renderer->renderShape(rd); if (cmp) renderer->endComposite(cmp); return ret; diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h index f6faec2d53..746b85bea6 100644 --- a/thirdparty/thorvg/src/renderer/tvgText.h +++ b/thirdparty/thorvg/src/renderer/tvgText.h @@ -26,12 +26,7 @@ #include <cstring> #include "tvgShape.h" #include "tvgFill.h" - -#ifdef THORVG_TTF_LOADER_SUPPORT - #include "tvgTtfLoader.h" -#else - #include "tvgLoader.h" -#endif +#include "tvgLoader.h" struct Text::Impl { @@ -69,6 +64,11 @@ struct Text::Impl auto loader = LoaderMgr::loader(name); if (!loader) return Result::InsufficientCondition; + if (style && strstr(style, "italic")) italic = true; + else italic = false; + + fontSize = size; + //Same resource has been loaded. if (this->loader == loader) { this->loader->sharing--; //make it sure the reference counting. @@ -78,8 +78,6 @@ struct Text::Impl } this->loader = static_cast<FontLoader*>(loader); - fontSize = size; - if (style && strstr(style, "italic")) italic = true; changed = true; return Result::Success; } @@ -91,6 +89,7 @@ struct Text::Impl bool render(RenderMethod* renderer) { + renderer->blend(paint->blend(), true); return PP(shape)->render(renderer); } @@ -98,13 +97,13 @@ struct Text::Impl { if (!loader) return false; + loader->request(shape, utf8); //reload if (changed) { - loader->request(shape, utf8, italic); loader->read(); changed = false; } - return loader->resize(shape, fontSize, fontSize); + return loader->transform(shape, fontSize, italic); } RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, TVG_UNUSED bool clipper) diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index 7ff07fe4c2..51dc156661 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,16 +1,22 @@ #!/bin/bash -e -VERSION=0.14.7 +VERSION=0.14.8 +# Uncomment and set a git hash to use specific commit instead of tag. +#GIT_COMMIT= -cd thirdparty/thorvg/ || true +pushd "$(dirname "$0")" rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ mkdir tmp/ && pushd tmp/ # Release -curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz -# Current Github main branch tip -#curl -L -O https://github.com/thorvg/thorvg/archive/refs/heads/main.tar.gz +if [ ! -z "$GIT_COMMIT" ]; then + echo "Updating ThorVG to commit:" $GIT_COMMIT + curl -L -O https://github.com/thorvg/thorvg/archive/$GIT_COMMIT.tar.gz +else + echo "Updating ThorVG to tagged release:" $VERSION + curl -L -O https://github.com/thorvg/thorvg/archive/v$VERSION.tar.gz +fi tar --strip-components=1 -xvf *.tar.gz rm *.tar.gz @@ -70,4 +76,4 @@ cp -rv src/loaders/jpg ../src/loaders/ popd rm -rf tmp - +popd |