diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 52e779f1bb0..05f408c1d28 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -487,7 +487,7 @@ License: Expat Files: ./thirdparty/zlib/ Comment: zlib -Copyright: 1995-2023, Jean-loup Gailly and Mark Adler +Copyright: 1995-2024, Jean-loup Gailly and Mark Adler License: Zlib Files: ./thirdparty/zstd/ diff --git a/core/io/image.cpp b/core/io/image.cpp index 9bb987b670c..d5c246bf2c0 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -509,6 +509,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p } void Image::convert(Format p_new_format) { + ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum."); if (data.size() == 0) { return; } diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml index bb354320b3f..c0acaae45fc 100644 --- a/doc/classes/Animation.xml +++ b/doc/classes/Animation.xml @@ -8,21 +8,23 @@ [codeblocks] [gdscript] # This creates an animation that makes the node "Enemy" move to the right by - # 100 pixels in 0.5 seconds. + # 100 pixels in 2.0 seconds. var animation = Animation.new() var track_index = animation.add_track(Animation.TYPE_VALUE) animation.track_set_path(track_index, "Enemy:position:x") animation.track_insert_key(track_index, 0.0, 0) - animation.track_insert_key(track_index, 0.5, 100) + animation.track_insert_key(track_index, 2.0, 100) + animation.length = 2.0 [/gdscript] [csharp] // This creates an animation that makes the node "Enemy" move to the right by - // 100 pixels in 0.5 seconds. + // 100 pixels in 2.0 seconds. var animation = new Animation(); int trackIndex = animation.AddTrack(Animation.TrackType.Value); animation.TrackSetPath(trackIndex, "Enemy:position:x"); animation.TrackInsertKey(trackIndex, 0.0f, 0); - animation.TrackInsertKey(trackIndex, 0.5f, 100); + animation.TrackInsertKey(trackIndex, 2.0f, 100); + animation.Length = 2.0f; [/csharp] [/codeblocks] Animations are just data containers, and must be added to nodes such as an [AnimationPlayer] to be played back. Animation tracks have different types, each with its own set of dedicated methods. Check [enum TrackType] to see available types. diff --git a/doc/classes/CollisionPolygon2D.xml b/doc/classes/CollisionPolygon2D.xml index f7dd2498564..3735526fe14 100644 --- a/doc/classes/CollisionPolygon2D.xml +++ b/doc/classes/CollisionPolygon2D.xml @@ -25,6 +25,7 @@ The polygon's list of vertices. Each point will be connected to the next, and the final point will be connected to the first. + [b]Note:[/b] The returned vertices are in the local coordinate space of the given [CollisionPolygon2D]. [b]Warning:[/b] The returned value is a clone of the [PackedVector2Array], not a reference. diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 0a5281f1d05..bae44d8c75e 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -18,6 +18,7 @@ + Adjusts this image's [param brightness], [param contrast], and [param saturation] by the given values. Does not work if the image is compressed (see [method is_compressed]). @@ -162,6 +163,7 @@ + Returns the color channels used by this image, as one of the [enum UsedChannels] constants. If the image is compressed, the original [param source] must be specified. @@ -572,6 +574,7 @@ OpenGL texture format [code]RGBA[/code] with four components, each with a bitdepth of 4. + OpenGL texture format [code]RGB[/code] with three components. Red and blue have a bitdepth of 5, and green has a bitdepth of 6. OpenGL texture format [code]GL_R32F[/code] where there's one component, a 32-bit floating-point value. @@ -656,8 +659,10 @@ [b]Note:[/b] When creating an [ImageTexture], an sRGB to linear color space conversion is performed. + [url=https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC]Ericsson Texture Compression format 2[/url] ([code]RGBA8[/code] variant), which compresses RA data and interprets it as two channels (red and green). See also [constant FORMAT_ETC2_RGBA8]. + The [url=https://en.wikipedia.org/wiki/S3_Texture_Compression]S3TC[/url] texture format also known as Block Compression 3 or BC3, which compresses RA data and interprets it as two channels (red and green). See also [constant FORMAT_DXT5]. [url=https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression]Adaptive Scalable Texutre Compression[/url]. This implements the 4x4 (high quality) mode. @@ -721,16 +726,22 @@ Represents the size of the [enum CompressMode] enum. + The image only uses one channel for luminance (grayscale). + The image uses two channels for luminance and alpha, respectively. + The image only uses the red channel. + The image uses two channels for red and green. + The image uses three channels for red, green, and blue. + The image uses four channels for red, green, blue, and alpha. Source texture (before compression) is a regular texture. Default for all textures. diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml index 3926ea8bebe..f85ab5c65ba 100644 --- a/doc/classes/PackedByteArray.xml +++ b/doc/classes/PackedByteArray.xml @@ -174,6 +174,7 @@ Returns a new [PackedByteArray] with the data decompressed. Set [param buffer_size] to the size of the uncompressed data. Set the compression mode using one of [enum FileAccess.CompressionMode]'s constants. + [b]Note:[/b] Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header. @@ -184,6 +185,7 @@ Returns a new [PackedByteArray] with the data decompressed. Set the compression mode using one of [enum FileAccess.CompressionMode]'s constants. [b]This method only accepts brotli, gzip, and deflate compression modes.[/b] This method is potentially slower than [code]decompress[/code], as it may have to re-allocate its output buffer multiple times while decompressing, whereas [code]decompress[/code] knows it's output buffer size from the beginning. GZIP has a maximal compression ratio of 1032:1, meaning it's very possible for a small compressed payload to decompress to a potentially very large output. To guard against this, you may provide a maximum size this function is allowed to allocate in bytes via [param max_output_size]. Passing -1 will allow for unbounded output. If any positive value is passed, and the decompression exceeds that amount in bytes, then an error will be returned. + [b]Note:[/b] Decompression is not guaranteed to work with data not compressed by Godot, for example if data compressed with the deflate compression mode lacks a checksum or header. diff --git a/doc/classes/PrimitiveMesh.xml b/doc/classes/PrimitiveMesh.xml index 7bcf3ce46b6..e468e3ef547 100644 --- a/doc/classes/PrimitiveMesh.xml +++ b/doc/classes/PrimitiveMesh.xml @@ -12,6 +12,7 @@ + Override this method to customize how this primitive mesh should be generated. Should return an [Array] where each element is another Array of values required for the mesh (see the [enum Mesh.ArrayType] constants). diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 977c0c0979f..8d7671af24d 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -774,7 +774,9 @@ Main window can't be focused. No-focus window will ignore all input, except mouse clicks. - Allows the window to be resizable by default. + If [code]true[/code], allows the window to be resizable by default. + [b]Note:[/b] This property is only read when the project starts. To change whether the window is resizable at runtime, set [member Window.unresizable] instead on the root Window, which can be retrieved using [code]get_viewport().get_window()[/code]. [member Window.unresizable] takes the opposite value of this setting. + [b]Note:[/b] Certain window managers can be configured to ignore the non-resizable status of a window. Do not rely on this setting as a guarantee that the window will [i]never[/i] be resizable. [b]Note:[/b] This setting is ignored on iOS. @@ -2467,6 +2469,7 @@ [b]Note:[/b] This setting is only effective when using the Compatibility rendering method, not Forward+ and Mobile. + The minimum number of instances that must be present in a scene to enable culling computations on multiple threads. If a scene has fewer instances than this number, culling is done on a single thread. diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index bcf1e364e97..872b73da8e8 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -74,7 +74,7 @@ - Returns the collision point at which the ray intersects the closest object. + Returns the collision point at which the ray intersects the closest object. If [member hit_from_inside] is [code]true[/code] and the ray starts inside of a collision shape, this function will return the origin point of the ray. [b]Note:[/b] This point is in the [b]global[/b] coordinate system. diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml index fa2badb1db3..231fdf590ef 100644 --- a/doc/classes/RayCast3D.xml +++ b/doc/classes/RayCast3D.xml @@ -75,7 +75,7 @@ - Returns the collision point at which the ray intersects the closest object. + Returns the collision point at which the ray intersects the closest object. If [member hit_from_inside] is [code]true[/code] and the ray starts inside of a collision shape, this function will return the origin point of the ray. [b]Note:[/b] This point is in the [b]global[/b] coordinate system. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index f2224abc8e9..5c801ba7abf 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -918,6 +918,14 @@ [b]Note:[/b] When using the OpenGL backend or when running in headless mode, this function always returns [code]null[/code]. + + + + + Returns the bounding rectangle for a canvas item in local space, as calculated by the renderer. This bound is used internally for culling. + [b]Warning:[/b] This function is intended for debugging in the editor, and will pass through and return a zero [Rect2] in exported projects. + + diff --git a/doc/classes/RichTextEffect.xml b/doc/classes/RichTextEffect.xml index 7d91987e7f9..8467e4e1149 100644 --- a/doc/classes/RichTextEffect.xml +++ b/doc/classes/RichTextEffect.xml @@ -4,7 +4,7 @@ A custom effect for a [RichTextLabel]. - A custom effect for a [RichTextLabel]. + A custom effect for a [RichTextLabel], which can be loaded in the [RichTextLabel] inspector or using [method RichTextLabel.install_effect]. [b]Note:[/b] For a [RichTextEffect] to be usable, a BBCode tag must be defined as a member variable called [code]bbcode[/code] in the script. [codeblocks] [gdscript] diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml index 6fbdb4faadb..623fddcb4ce 100644 --- a/doc/classes/RichTextLabel.xml +++ b/doc/classes/RichTextLabel.xml @@ -218,7 +218,28 @@ - Installs a custom effect. [param effect] should be a valid [RichTextEffect]. + Installs a custom effect. This can also be done in the RichTextLabel inspector using the [member custom_effects] property. [param effect] should be a valid [RichTextEffect]. + Example RichTextEffect: + [codeblock] + # effect.gd + class_name MyCustomEffect + extends RichTextEffect + + var bbcode = "my_custom_effect" + + # ... + [/codeblock] + Registering the above effect in RichTextLabel from script: + [codeblock] + # rich_text_label.gd + extends RichTextLabel + + func _ready(): + install_effect(MyCustomEffect.new()) + + # Alternatively, if not using `class_name` in the script that extends RichTextEffect: + install_effect(preload("res://effect.gd").new()) + [/codeblock] diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index a3f0a6c734a..7184928d71f 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -7,7 +7,6 @@ [Skeleton3D] provides an interface for managing a hierarchy of bones, including pose, rest and animation (see [Animation]). It can also use ragdoll physics. The overall transform of a bone with respect to the skeleton is determined by bone pose. Bone rest defines the initial transform of the bone pose. Note that "global pose" below refers to the overall transform of the bone with respect to skeleton, so it is not the actual global/world transform of the bone. - To setup different types of inverse kinematics, consider using [SkeletonIK3D], or add a custom IK implementation in [method Node._process] as a child node. https://godotengine.org/asset-library/asset/523 diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index c66b64f4bea..7d42d2b621b 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -41,6 +41,7 @@ + Returns the internal [RID] used by the [PhysicsServer3D] for this body. @@ -100,11 +101,14 @@ [b]Note:[/b] Object A can detect a contact with object B only if object B is in any of the layers that object A scans. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. + The body's damping coefficient. Higher values will slow down the body more noticeably when forces are applied. Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes. + The body's drag coefficient. Higher values increase this body's air resistance. + [b]Note:[/b] This value is currently unused by Godot's default physics implementation. Higher values will result in a stiffer body, while lower values will increase the body's ability to bend. The value can be between [code]0.0[/code] and [code]1.0[/code] (inclusive). @@ -113,6 +117,7 @@ [NodePath] to a [CollisionObject3D] this SoftBody3D should avoid clipping. + The pressure coefficient of this soft body. Simulate pressure build-up from inside this body. Higher values increase the strength of this effect. If [code]true[/code], the [SoftBody3D] will respond to [RayCast3D]s. diff --git a/doc/classes/String.xml b/doc/classes/String.xml index d2493c237b7..702d7298650 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -96,13 +96,14 @@ [gdscript] "move_local_x".capitalize() # Returns "Move Local X" "sceneFile_path".capitalize() # Returns "Scene File Path" + "2D, FPS, PNG".capitalize() # Returns "2d, Fps, Png" [/gdscript] [csharp] "move_local_x".Capitalize(); // Returns "Move Local X" "sceneFile_path".Capitalize(); // Returns "Scene File Path" + "2D, FPS, PNG".Capitalize(); // Returns "2d, Fps, Png" [/csharp] [/codeblocks] - [b]Note:[/b] This method not the same as the default appearance of properties in the Inspector dock, as it does not capitalize acronyms ([code]"2D"[/code], [code]"FPS"[/code], [code]"PNG"[/code], etc.) as you may expect. @@ -943,7 +944,7 @@ - Returns the string converted to lowercase. + Returns the string converted to [code]lowercase[/code]. @@ -956,12 +957,25 @@ Returns the string converted to [code]snake_case[/code]. + [b]Note:[/b] Numbers followed by a [i]single[/i] letter are not separated in the conversion to keep some words (such as "2D") together. + [codeblocks] + [gdscript] + "Node2D".to_snake_case() # Returns "node_2d" + "2nd place".to_snake_case() # Returns "2_nd_place" + "Texture3DAssetFolder".to_snake_case() # Returns "texture_3d_asset_folder" + [/gdscript] + [csharp] + "Node2D".ToSnakeCase(); // Returns "node_2d" + "2nd place".ToSnakeCase(); // Returns "2_nd_place" + "Texture3DAssetFolder".ToSnakeCase(); // Returns "texture_3d_asset_folder" + [/csharp] + [/codeblocks] - Returns the string converted to uppercase. + Returns the string converted to [code]UPPERCASE[/code]. diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index c78039dd349..46f78105b8c 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -8,6 +8,7 @@ You will usually just pass a [String] to methods expecting a [StringName] and it will be automatically converted, but you may occasionally want to construct a [StringName] ahead of time with the [StringName] constructor or, in GDScript, the literal syntax [code]&"example"[/code]. See also [NodePath], which is a similar concept specifically designed to store pre-parsed scene tree paths. All of [String]'s methods are available in this class too. They convert the [StringName] into a string, and they also return a string. This is highly inefficient and should only be used if the string is desired. + [b]Note:[/b] In C#, an explicit conversion to [code]System.String[/code] is required to use the methods listed on this page. Use the [code]ToString()[/code] method to cast a [StringName] to a string, and then use the equivalent methods in [code]System.String[/code] or [code]StringExtensions[/code]. [b]Note:[/b] In a boolean context, a [StringName] will evaluate to [code]false[/code] if it is empty ([code]StringName("")[/code]). Otherwise, a [StringName] will always evaluate to [code]true[/code]. The [code]not[/code] operator cannot be used. Instead, [method is_empty] should be used to check for empty [StringName]s. @@ -90,13 +91,14 @@ [gdscript] "move_local_x".capitalize() # Returns "Move Local X" "sceneFile_path".capitalize() # Returns "Scene File Path" + "2D, FPS, PNG".capitalize() # Returns "2d, Fps, Png" [/gdscript] [csharp] "move_local_x".Capitalize(); // Returns "Move Local X" "sceneFile_path".Capitalize(); // Returns "Scene File Path" + "2D, FPS, PNG".Capitalize(); // Returns "2d, Fps, Png" [/csharp] [/codeblocks] - [b]Note:[/b] This method not the same as the default appearance of properties in the Inspector dock, as it does not capitalize acronyms ([code]"2D"[/code], [code]"FPS"[/code], [code]"PNG"[/code], etc.) as you may expect. @@ -850,7 +852,7 @@ - Returns the string converted to lowercase. + Returns the string converted to [code]lowercase[/code]. @@ -863,12 +865,25 @@ Returns the string converted to [code]snake_case[/code]. + [b]Note:[/b] Numbers followed by a [i]single[/i] letter are not separated in the conversion to keep some words (such as "2D") together. + [codeblocks] + [gdscript] + "Node2D".to_snake_case() # Returns "node_2d" + "2nd place".to_snake_case() # Returns "2_nd_place" + "Texture3DAssetFolder".to_snake_case() # Returns "texture_3d_asset_folder" + [/gdscript] + [csharp] + "Node2D".ToSnakeCase(); // Returns "node_2d" + "2nd place".ToSnakeCase(); // Returns "2_nd_place" + "Texture3DAssetFolder".ToSnakeCase(); // Returns "texture_3d_asset_folder" + [/csharp] + [/codeblocks] - Returns the string converted to uppercase. + Returns the string converted to [code]UPPERCASE[/code]. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 17805f259cd..8bf12e8ea4f 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1647,6 +1647,7 @@ Vertical BGR subpixel layout. + Represents the size of the [enum FontLCDSubpixelLayout] enum. Text direction is determined based on contents and current locale. @@ -1770,6 +1771,7 @@ Determines whether the ellipsis at the end of the text is enforced and may not be hidden. + Accounts for the text being justified before attempting to trim it (see [enum JustificationFlag]). Grapheme is supported by the font, and can be drawn. @@ -1908,6 +1910,7 @@ Spacing at the bottom of the line. + Represents the size of the [enum SpacingType] enum. Font is bold. diff --git a/doc/classes/VehicleWheel3D.xml b/doc/classes/VehicleWheel3D.xml index c4ebed84c5a..b110eb29210 100644 --- a/doc/classes/VehicleWheel3D.xml +++ b/doc/classes/VehicleWheel3D.xml @@ -81,7 +81,7 @@ This is the distance in meters the wheel is lowered from its origin point. Don't set this to 0.0 and move the wheel into position, instead move the origin point of your wheel (the gizmo in Godot) to the position the wheel will take when bottoming out, then use the rest length to move the wheel down to the position it should be in when the car is in rest. - This value affects the roll of your vehicle. If set to 1.0 for all wheels, your vehicle will be prone to rolling over, while a value of 0.0 will resist body roll. + This value affects the roll of your vehicle. If set to 1.0 for all wheels, your vehicle will resist body roll, while a value of 0.0 will be prone to rolling over. diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index e78d13c462d..101bad8c1e1 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -774,6 +774,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou _render_batch(p_lights, i); } + glDisable(GL_SCISSOR_TEST); state.current_batch_index = 0; state.canvas_instance_batches.clear(); state.last_item_index += index; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index aa68febec8f..69c1982935b 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -434,8 +434,7 @@ layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; - /* Material Uniforms */ - +/* Material Uniforms */ #ifdef MATERIAL_UNIFORMS_USED /* clang-format off */ diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index a96a491517f..e8a1bf55579 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -248,6 +248,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. + s->mesh_to_skeleton_xform = p_surface.mesh_to_skeleton_xform; if (p_surface.skin_data.size() || mesh->blend_shape_count > 0) { // Size must match the size of the vertex array. @@ -471,6 +472,7 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; + sd.mesh_to_skeleton_xform = s.mesh_to_skeleton_xform; if (mesh->blend_shape_count) { sd.blend_shape_data = Vector(); @@ -522,15 +524,16 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { for (uint32_t i = 0; i < mesh->surface_count; i++) { AABB laabb; - if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { - int bs = mesh->surfaces[i]->bone_aabbs.size(); - const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + const Mesh::Surface &surface = *mesh->surfaces[i]; + if ((surface.format & RS::ARRAY_FORMAT_BONES) && surface.bone_aabbs.size()) { + int bs = surface.bone_aabbs.size(); + const AABB *skbones = surface.bone_aabbs.ptr(); int sbs = skeleton->size; ERR_CONTINUE(bs > sbs); const float *baseptr = skeleton->data.ptr(); - bool first = true; + bool found_bone_aabb = false; if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { @@ -550,11 +553,13 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[1][1] = dataptr[5]; mtx.origin.y = dataptr[7]; - AABB baabb = mtx.xform(skbones[j]); + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); - if (first) { + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } @@ -582,21 +587,29 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[2][2] = dataptr[10]; mtx.origin.z = dataptr[11]; - AABB baabb = mtx.xform(skbones[j]); - if (first) { + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); + + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } } } + if (found_bone_aabb) { + // Transform skeleton bounds back to mesh's space if any animated AABB applied. + laabb = surface.mesh_to_skeleton_xform.affine_inverse().xform(laabb); + } + if (laabb.size == Vector3()) { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } } else { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } if (i == 0) { diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index f9122771f9d..a0d7fe88863 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -103,6 +103,10 @@ struct Mesh { Vector bone_aabbs; + // Transform used in runtime bone AABBs compute. + // As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + struct BlendShape { GLuint vertex_buffer = 0; GLuint vertex_array = 0; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index d06a78f0494..c4694541b2f 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -6713,24 +6713,24 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu return OK; } -Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector &p_storage_textures) { +Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector &p_storage_textures, bool p_constrained_to_region) { VkRenderPassBeginInfo render_pass_begin; render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_begin.pNext = nullptr; render_pass_begin.renderPass = render_pass; render_pass_begin.framebuffer = vkframebuffer; - /* - * Given how API works, it makes sense to always fully operate on the whole framebuffer. - * This allows better continue operations for operations like shadowmapping. - render_pass_begin.renderArea.extent.width = viewport_size.width; - render_pass_begin.renderArea.extent.height = viewport_size.height; - render_pass_begin.renderArea.offset.x = viewport_offset.x; - render_pass_begin.renderArea.offset.y = viewport_offset.y; - */ - render_pass_begin.renderArea.extent.width = framebuffer->size.width; - render_pass_begin.renderArea.extent.height = framebuffer->size.height; - render_pass_begin.renderArea.offset.x = 0; - render_pass_begin.renderArea.offset.y = 0; + + if (p_constrained_to_region) { + render_pass_begin.renderArea.extent.width = viewport_size.width; + render_pass_begin.renderArea.extent.height = viewport_size.height; + render_pass_begin.renderArea.offset.x = viewport_offset.x; + render_pass_begin.renderArea.offset.y = viewport_offset.y; + } else { + render_pass_begin.renderArea.extent.width = framebuffer->size.width; + render_pass_begin.renderArea.extent.height = framebuffer->size.height; + render_pass_begin.renderArea.offset.x = 0; + render_pass_begin.renderArea.offset.y = 0; + } Vector clear_values; clear_values.resize(framebuffer->texture_ids.size()); @@ -6880,6 +6880,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu Point2i viewport_offset; Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; bool needs_clear_color = false; bool needs_clear_depth = false; @@ -6894,21 +6895,30 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu viewport_offset = regioni.position; viewport_size = regioni.size; - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; + + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } } } @@ -6935,7 +6945,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu ERR_FAIL_COND_V(err != OK, INVALID_ID); VkCommandBuffer command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures, constrained_to_region); if (err != OK) { return INVALID_ID; @@ -6951,6 +6961,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu draw_list_current_subpass = 0; if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); } @@ -6989,6 +7000,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p Point2i viewport_offset; Point2i viewport_size = framebuffer->size; + bool constrained_to_region = false; bool needs_clear_color = false; bool needs_clear_depth = false; @@ -7004,13 +7016,29 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p viewport_offset = regioni.position; viewport_size = regioni.size; - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; + // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears + // and we constrain the render area to the region. + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + constrained_to_region = true; + p_initial_color_action = INITIAL_ACTION_CLEAR; + p_initial_depth_action = INITIAL_ACTION_CLEAR; + } else { + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_CONTINUE; + } + if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_color = true; + p_initial_color_action = INITIAL_ACTION_KEEP; + } + if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { + needs_clear_depth = true; + p_initial_depth_action = INITIAL_ACTION_KEEP; + } } } @@ -7036,7 +7064,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures, constrained_to_region); if (err != OK) { return ERR_CANT_CREATE; @@ -7056,6 +7084,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p } if (needs_clear_color || needs_clear_depth) { + DEV_ASSERT(!constrained_to_region); _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 9c621c1d441..b43c9d03d89 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -159,7 +159,7 @@ class RenderingDeviceVulkan : public RenderingDevice { RID owner; }; - RID_Owner texture_owner; + RID_Owner texture_owner; uint32_t texture_upload_region_size_px = 0; Vector _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false); @@ -409,7 +409,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t view_count; }; - RID_Owner framebuffer_owner; + RID_Owner framebuffer_owner; /***********************/ /**** VERTEX BUFFER ****/ @@ -424,7 +424,7 @@ class RenderingDeviceVulkan : public RenderingDevice { // This mapping is done here internally, and it's not // exposed. - RID_Owner vertex_buffer_owner; + RID_Owner vertex_buffer_owner; struct VertexDescriptionKey { Vector vertex_formats; @@ -506,7 +506,7 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector offsets; }; - RID_Owner vertex_array_owner; + RID_Owner vertex_array_owner; struct IndexBuffer : public Buffer { uint32_t max_index = 0; // Used for validation. @@ -515,7 +515,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool supports_restart_indices = false; }; - RID_Owner index_buffer_owner; + RID_Owner index_buffer_owner; struct IndexArray { uint32_t max_index = 0; // Remember the maximum index here too, for validation. @@ -526,7 +526,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool supports_restart_indices = false; }; - RID_Owner index_array_owner; + RID_Owner index_array_owner; /****************/ /**** SHADER ****/ @@ -649,7 +649,7 @@ class RenderingDeviceVulkan : public RenderingDevice { String _shader_uniform_debug(RID p_shader, int p_set = -1); - RID_Owner shader_owner; + RID_Owner shader_owner; /******************/ /**** UNIFORMS ****/ @@ -713,8 +713,8 @@ class RenderingDeviceVulkan : public RenderingDevice { DescriptorPool *_descriptor_pool_allocate(const DescriptorPoolKey &p_key); void _descriptor_pool_free(const DescriptorPoolKey &p_key, DescriptorPool *p_pool); - RID_Owner uniform_buffer_owner; - RID_Owner storage_buffer_owner; + RID_Owner uniform_buffer_owner; + RID_Owner storage_buffer_owner; // Texture buffer needs a view. struct TextureBuffer { @@ -722,7 +722,7 @@ class RenderingDeviceVulkan : public RenderingDevice { VkBufferView view = VK_NULL_HANDLE; }; - RID_Owner texture_buffer_owner; + RID_Owner texture_buffer_owner; // This structure contains the descriptor set. They _need_ to be allocated // for a shader (and will be erased when this shader is erased), but should @@ -752,7 +752,7 @@ class RenderingDeviceVulkan : public RenderingDevice { void *invalidated_callback_userdata = nullptr; }; - RID_Owner uniform_set_owner; + RID_Owner uniform_set_owner; /*******************/ /**** PIPELINES ****/ @@ -791,7 +791,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t push_constant_stages_mask = 0; }; - RID_Owner render_pipeline_owner; + RID_Owner render_pipeline_owner; struct PipelineCacheHeader { uint32_t magic; @@ -828,7 +828,7 @@ class RenderingDeviceVulkan : public RenderingDevice { uint32_t local_group_size[3] = { 0, 0, 0 }; }; - RID_Owner compute_pipeline_owner; + RID_Owner compute_pipeline_owner; /*******************/ /**** DRAW LIST ****/ @@ -929,7 +929,7 @@ class RenderingDeviceVulkan : public RenderingDevice { void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector &p_storage_textures); + Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector &p_storage_textures, bool p_constrained_to_region); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access, BitField p_post_barrier); Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index eeeb6ee689e..bdaef82d6f0 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -557,13 +557,11 @@ Error AudioDriverWASAPI::init() { target_latency_ms = GLOBAL_GET("audio/driver/output_latency"); - Error err = init_output_device(); - if (err != OK) { - ERR_PRINT("WASAPI: init_output_device error"); - } - exit_thread.clear(); + Error err = init_output_device(); + ERR_FAIL_COND_V_MSG(err != OK, err, "WASAPI: init_output_device error."); + thread.start(thread_func, this); return OK; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index c3f176b4246..c5285179d4d 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -2148,6 +2148,9 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMapfiles[cpos]->modified_time = FileAccess::get_modified_time(p_file); fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import"); @@ -2342,7 +2345,13 @@ void EditorFileSystem::reimport_files(const Vector &p_files) { Error EditorFileSystem::reimport_append(const String &p_file, const HashMap &p_custom_options, const String &p_custom_importer, Variant p_generator_parameters) { ERR_FAIL_COND_V_MSG(!importing, ERR_INVALID_PARAMETER, "Can only append files to import during a current reimport process."); - return _reimport_file(p_file, p_custom_options, p_custom_importer, &p_generator_parameters); + Error ret = _reimport_file(p_file, p_custom_options, p_custom_importer, &p_generator_parameters); + + // Emit the resource_reimported signal for the single file we just reimported. + Vector reloads; + reloads.append(p_file); + emit_signal(SNAME("resources_reimported"), reloads); + return ret; } Error EditorFileSystem::_resource_import(const String &p_path) { diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 75a56ba2668..3fabe36bd36 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2925,6 +2925,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SET_RENDERER_NAME_SAVE_AND_RESTART: { ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method", renderer_request); + if (renderer_request == "mobile" || renderer_request == "gl_compatibility") { + // Also change the mobile override if changing to a compatible rendering method. + // This prevents visual discrepancies between desktop and mobile platforms. + ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", renderer_request); + } else if (renderer_request == "forward_plus") { + // Use the equivalent mobile rendering method. This prevents the rendering method from staying + // on its old choice if moving from `gl_compatibility` to `forward_plus`. + ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", "mobile"); + } + ProjectSettings::get_singleton()->save(); save_all_scenes(); @@ -6571,6 +6581,9 @@ void EditorNode::_renderer_selected(int p_which) { } renderer_request = rendering_method; + video_restart_dialog->set_text( + vformat(TTR("Changing the renderer requires restarting the editor.\n\nChoosing Save & Restart will change the rendering method to:\n- Desktop platforms: %s\n- Mobile platforms: %s\n- Web platform: gl_compatibility"), + renderer_request, renderer_request.replace("forward_plus", "mobile"))); video_restart_dialog->popup_centered(); renderer->select(renderer_current); _update_renderer_color(); @@ -7583,7 +7596,7 @@ EditorNode::EditorNode() { renderer->connect("item_selected", callable_mp(this, &EditorNode::_renderer_selected)); renderer->add_theme_font_override("font", gui_base->get_theme_font(SNAME("bold"), SNAME("EditorFonts"))); renderer->add_theme_font_size_override("font_size", gui_base->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"))); - renderer->set_tooltip_text(TTR("Choose a renderer.")); + renderer->set_tooltip_text(TTR("Choose a rendering method.\n\nNotes:\n- On mobile platforms, the Mobile rendering method is used if Forward+ is selected here.\n- On the web platform, the Compatibility rendering method is always used.")); right_menu_hb->add_child(renderer); @@ -7628,7 +7641,6 @@ EditorNode::EditorNode() { _update_renderer_color(); video_restart_dialog = memnew(ConfirmationDialog); - video_restart_dialog->set_text(TTR("Changing the renderer requires restarting the editor.")); video_restart_dialog->set_ok_button_text(TTR("Save & Restart")); video_restart_dialog->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind(SET_RENDERER_NAME_SAVE_AND_RESTART)); gui_base->add_child(video_restart_dialog); diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index e551b0531af..0fb6eaaac8b 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -465,6 +465,13 @@ bool ExportTemplateManager::_install_file_selected(const String &p_file, bool p_ break; } + if (String::utf8(fname).ends_with("/")) { + // File is a directory, ignore it. + // Directories will be created when extracting each file. + ret = unzGoToNextFile(pkg); + continue; + } + String file_path(String::utf8(fname).simplify_path()); String file = file_path.get_file(); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 4b379807b67..e0148e8030a 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -536,7 +536,7 @@ void ScriptEditor::_set_breakpoint(Ref p_script, int p_line, bool p_ } state["breakpoints"] = breakpoints; script_editor_cache->set_value(scr->get_path(), "state", state); - EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, false); + EditorDebuggerNode::get_singleton()->set_breakpoint(scr->get_path(), p_line + 1, p_enabled); } } diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index d6ed5eb995f..82462866cf6 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -1044,107 +1044,108 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { // get_object_of_execution { - { String base = "var roman = kieliszek."; - String expected = "kieliszek."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + String base = "var roman = kieliszek."; + String expected = "kieliszek."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); } - valid = valid && (got == expected); -} -{ - String base = "r."; - String expected = "r."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + { + String base = "r."; + String expected = "r."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); } - valid = valid && (got == expected); -} -{ - String base = "mortadela("; - String expected = ""; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + { + String base = "mortadela("; + String expected = ""; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); } - valid = valid && (got == expected); -} -{ - String base = "var node = $world/ukraine/lviv."; - String expected = "$world/ukraine/lviv."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + { + String base = "var node = $world/ukraine/lviv."; + String expected = "$world/ukraine/lviv."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); } - valid = valid && (got == expected); -} -} -// get_starting_space -{ - String base = "\t\t\t var roman = kieliszek."; - String expected = "\t\t\t"; - String got = get_starting_space(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -// Parse Arguments -{ - String line = "( )"; - Vector got_vector = parse_arguments(line); - String got = ""; - String expected = ""; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , b , c)"; - Vector got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||b|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , \"b,\" , c)"; - Vector got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||\"b,\"|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , \"(,),,,,\" , c)"; - Vector got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||\"(,),,,,\"|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -return valid; + // get_starting_space + { + String base = "\t\t\t var roman = kieliszek."; + String expected = "\t\t\t"; + String got = get_starting_space(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + + // Parse Arguments + { + String line = "( )"; + Vector got_vector = parse_arguments(line); + String got = ""; + String expected = ""; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , b , c)"; + Vector got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||b|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , \"b,\" , c)"; + Vector got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||\"b,\"|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , \"(,),,,,\" , c)"; + Vector got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||\"(,),,,,\"|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + + return valid; } // Validate in all arrays if names don't do cyclic renames "Node" -> "Node2D" | "Node2D" -> "2DNode" diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp index 952b07b870b..9c18872ccc7 100644 --- a/editor/renames_map_3_to_4.cpp +++ b/editor/renames_map_3_to_4.cpp @@ -163,6 +163,7 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = { // { "_set_name", "get_tracker_name" }, // XRPositionalTracker -- CameraFeed uses this. // { "_unhandled_input", "_unhandled_key_input" }, // BaseButton, ViewportContainer -- Breaks Node, FileDialog, SubViewportContainer. + // { "add_animation", "add_animation_library" }, // AnimationPlayer -- Breaks SpriteFrames (and isn't a correct conversion). // { "create_gizmo", "_create_gizmo" }, // EditorNode3DGizmoPlugin -- May be used. // { "get_dependencies", "_get_dependencies" }, // ResourceFormatLoader -- Breaks ResourceLoader. // { "get_extents", "get_size" }, // BoxShape, RectangleShape -- Breaks Decal, VoxelGI, GPUParticlesCollisionBox, GPUParticlesCollisionSDF, GPUParticlesCollisionHeightField, GPUParticlesAttractorBox, GPUParticlesAttractorVectorField, FogVolume @@ -214,8 +215,6 @@ const char *RenamesMap3To4::gdscript_function_renames[][2] = { { "_set_current", "set_current" }, // Camera2D { "_set_editor_description", "set_editor_description" }, // Node { "_toplevel_raise_self", "_top_level_raise_self" }, // CanvasItem - { "_update_wrap_at", "_update_wrap_at_column" }, // TextEdit - { "add_animation", "add_animation_library" }, // AnimationPlayer { "add_cancel", "add_cancel_button" }, // AcceptDialog { "add_central_force", "apply_central_force" }, //RigidBody2D { "add_child_below_node", "add_sibling" }, // Node @@ -643,8 +642,6 @@ const char *RenamesMap3To4::csharp_function_renames[][2] = { { "_SetEditorDescription", "SetEditorDescription" }, // Node { "_SetPlaying", "SetPlaying" }, // AnimatedSprite3D { "_ToplevelRaiseSelf", "_TopLevelRaiseSelf" }, // CanvasItem - { "_UpdateWrapAt", "_UpdateWrapAtColumn" }, // TextEdit - { "AddAnimation", "AddAnimationLibrary" }, // AnimationPlayer { "AddCancel", "AddCancelButton" }, // AcceptDialog { "AddCentralForce", "AddConstantCentralForce" }, //RigidBody2D { "AddChildBelowNode", "AddSibling" }, // Node diff --git a/misc/hooks/README.md b/misc/hooks/README.md index 0aaeff8ae32..573f8fe350b 100644 --- a/misc/hooks/README.md +++ b/misc/hooks/README.md @@ -29,7 +29,7 @@ so they should work out of the box on Linux/macOS. ##### clang-format - Download LLVM for Windows (version 13 or later) from - + - Make sure LLVM is added to the `PATH` during installation ##### black diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index fd0213c175e..e5cf0c4aa23 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -80,7 +80,7 @@ fi # To get consistent formatting, we recommend contributors to use the same # clang-format version as CI. RECOMMENDED_CLANG_FORMAT_MAJOR_MIN="13" -RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="15" +RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="16" if [ ! -x "$CLANG_FORMAT" ] ; then message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX." diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp index 86f3385ae33..2e0642ed5a8 100644 --- a/modules/basis_universal/register_types.cpp +++ b/modules/basis_universal/register_types.cpp @@ -221,7 +221,7 @@ static Ref basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size imgfmt = Image::FORMAT_DXT5_RA_AS_RG; } else if (RS::get_singleton()->has_os_feature("etc2")) { format = basist::transcoder_texture_format::cTFETC2; // get this from renderer - imgfmt = Image::FORMAT_ETC2_RGBA8; + imgfmt = Image::FORMAT_ETC2_RA_AS_RG; } else { //opengl most likely, bad for normal maps, nothing to do about this. format = basist::transcoder_texture_format::cTFRGBA32; diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp index a8d83e2490b..f5280870e07 100644 --- a/modules/dds/texture_loader_dds.cpp +++ b/modules/dds/texture_loader_dds.cpp @@ -220,8 +220,12 @@ Ref ResourceFormatDDS::load(const String &p_path, const String &p_orig //compressed bc uint32_t size = MAX(info.divisor, w) / info.divisor * MAX(info.divisor, h) / info.divisor * info.block_size; - ERR_FAIL_COND_V(size != pitch, Ref()); - ERR_FAIL_COND_V(!(flags & DDSD_LINEARSIZE), Ref()); + + if (flags & DDSD_LINEARSIZE) { + ERR_FAIL_COND_V_MSG(size != pitch, Ref(), "DDS header flags specify that a linear size of the top-level image is present, but the specified size does not match the expected value."); + } else { + ERR_FAIL_COND_V_MSG(pitch != 0, Ref(), "DDS header flags specify that no linear size will given for the top-level image, but a non-zero linear size value is present in the header."); + } for (uint32_t i = 1; i < mipmaps; i++) { w = MAX(1u, w >> 1); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index c3c7a4c4b0d..5e066db8312 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -3126,7 +3126,7 @@ void GLTFDocument::_parse_image_save_image(Ref p_state, const String bool must_import = true; Vector img_data = p_image->get_data(); Dictionary generator_parameters; - String file_path = p_state->get_base_path() + "/" + p_state->filename.get_basename() + "_" + p_image->get_name() + ".png"; + String file_path = p_state->get_base_path().path_join(p_state->filename.get_basename() + "_" + p_image->get_name()) + ".png"; if (FileAccess::exists(file_path + ".import")) { Ref config; config.instantiate(); @@ -3137,6 +3137,8 @@ void GLTFDocument::_parse_image_save_image(Ref p_state, const String if (!generator_parameters.has("md5")) { must_import = false; // Didn't come from a gltf document; don't overwrite. } + } + if (must_import) { String existing_md5 = generator_parameters["md5"]; unsigned char md5_hash[16]; CryptoCore::md5(img_data.ptr(), img_data.size(), md5_hash); diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp index 3e3118b1cd4..36f46ea371d 100644 --- a/modules/multiplayer/scene_multiplayer.cpp +++ b/modules/multiplayer/scene_multiplayer.cpp @@ -483,9 +483,14 @@ Error SceneMultiplayer::complete_auth(int p_peer) { ERR_FAIL_COND_V(!pending_peers.has(p_peer), ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(pending_peers[p_peer].local, ERR_FILE_CANT_WRITE, "The authentication session was already marked as completed."); pending_peers[p_peer].local = true; + // Notify the remote peer that the authentication has completed. uint8_t buf[2] = { NETWORK_COMMAND_SYS, SYS_COMMAND_AUTH }; + multiplayer_peer->set_target_peer(p_peer); + multiplayer_peer->set_transfer_channel(0); + multiplayer_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); Error err = _send(buf, 2); + // The remote peer already reported the authentication as completed, so admit the peer. // May generate new packets, so it must happen after sending confirmation. if (pending_peers[p_peer].remote) { diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 233a168a00d..e2d0f71c122 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -456,7 +456,7 @@ String EditorExportPlatformAndroid::get_valid_basename() const { if (is_digit(c) && first) { continue; } - if (is_ascii_alphanumeric_char(c)) { + if (is_ascii_identifier_char(c)) { name += String::chr(c); first = false; } @@ -533,13 +533,6 @@ bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, return false; } - if (p_package.find("$genname") >= 0 && !is_project_name_valid()) { - if (r_error) { - *r_error = TTR("The project name does not meet the requirement for the package name format. Please explicitly specify the package name."); - } - return false; - } - return true; } @@ -2435,6 +2428,13 @@ bool EditorExportPlatformAndroid::has_valid_project_configuration(const Refget("package/unique_name"); + if (package_name.find("$genname") >= 0 && !is_project_name_valid()) { + // Warning only, so don't override `valid`. + err += vformat(TTR("The project name does not meet the requirement for the package name format and will be updated to \"%s\". Please explicitly specify the package name if needed."), get_valid_basename()); + err += "\n"; + } + r_error = err; return valid; } diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index ebd83da3770..11f45627bcd 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -101,7 +101,12 @@ void Polygon2D::_skeleton_bone_setup_changed() { } void Polygon2D::_notification(int p_what) { + if (p_what == NOTIFICATION_TRANSFORM_CHANGED && !Engine::get_singleton()->is_editor_hint()) { + return; // Mesh recreation for NOTIFICATION_TRANSFORM_CHANGED is only needed in editor. + } + switch (p_what) { + case NOTIFICATION_TRANSFORM_CHANGED: case NOTIFICATION_DRAW: { if (polygon.size() < 3) { return; @@ -364,7 +369,30 @@ void Polygon2D::_notification(int p_what) { arr[RS::ARRAY_INDEX] = index_array; - RS::get_singleton()->mesh_add_surface_from_arrays(mesh, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); + RS::SurfaceData sd; + + if (skeleton_node) { + // Compute transform between mesh and skeleton for runtime AABB compute. + const Transform2D mesh_transform = get_global_transform(); + const Transform2D skeleton_transform = skeleton_node->get_global_transform(); + const Transform2D mesh_to_sk2d = skeleton_transform.affine_inverse() * mesh_transform; + + // Convert 2d transform to 3d. + sd.mesh_to_skeleton_xform.basis.rows[0][0] = mesh_to_sk2d.columns[0][0]; + sd.mesh_to_skeleton_xform.basis.rows[1][0] = mesh_to_sk2d.columns[0][1]; + sd.mesh_to_skeleton_xform.origin.x = mesh_to_sk2d.get_origin().x; + + sd.mesh_to_skeleton_xform.basis.rows[0][1] = mesh_to_sk2d.columns[1][0]; + sd.mesh_to_skeleton_xform.basis.rows[1][1] = mesh_to_sk2d.columns[1][1]; + sd.mesh_to_skeleton_xform.origin.y = mesh_to_sk2d.get_origin().y; + } + + Error err = RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, arr, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES); + if (err != OK) { + return; + } + + RS::get_singleton()->mesh_add_surface(mesh, sd); RS::get_singleton()->canvas_item_add_mesh(get_canvas_item(), mesh, Transform2D(), Color(1, 1, 1), texture.is_valid() ? texture->get_rid() : RID()); } diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 6d8d60dcaa8..e315e5ea710 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -625,6 +625,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref 0) { shape_owner_remove_shape(p_owner, 0); } + + update_gizmos(); } uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const { diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 6ec8724a775..cf431282cf7 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -580,6 +580,8 @@ void TreeItem::set_custom_draw(int p_column, Object *p_object, const StringName cells.write[p_column].custom_draw_obj = p_object->get_instance_id(); cells.write[p_column].custom_draw_callback = p_callback; + + _changed_notify(p_column); } void TreeItem::set_collapsed(bool p_collapsed) { @@ -1290,8 +1292,14 @@ void TreeItem::clear_custom_color(int p_column) { void TreeItem::set_custom_font(int p_column, const Ref &p_font) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].custom_font == p_font) { + return; + } + cells.write[p_column].custom_font = p_font; cells.write[p_column].cached_minimum_size_dirty = true; + + _changed_notify(p_column); } Ref TreeItem::get_custom_font(int p_column) const { @@ -1302,8 +1310,14 @@ Ref TreeItem::get_custom_font(int p_column) const { void TreeItem::set_custom_font_size(int p_column, int p_font_size) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].custom_font_size == p_font_size) { + return; + } + cells.write[p_column].custom_font_size = p_font_size; cells.write[p_column].cached_minimum_size_dirty = true; + + _changed_notify(p_column); } int TreeItem::get_custom_font_size(int p_column) const { @@ -1352,8 +1366,14 @@ Color TreeItem::get_custom_bg_color(int p_column) const { void TreeItem::set_custom_as_button(int p_column, bool p_button) { ERR_FAIL_INDEX(p_column, cells.size()); + if (cells[p_column].custom_button == p_button) { + return; + } + cells.write[p_column].custom_button = p_button; cells.write[p_column].cached_minimum_size_dirty = true; + + _changed_notify(p_column); } bool TreeItem::is_custom_set_as_button(int p_column) const { diff --git a/servers/physics_2d/godot_area_pair_2d.cpp b/servers/physics_2d/godot_area_pair_2d.cpp index bd1540bac86..ca12e30c292 100644 --- a/servers/physics_2d/godot_area_pair_2d.cpp +++ b/servers/physics_2d/godot_area_pair_2d.cpp @@ -66,6 +66,7 @@ bool GodotAreaPair2D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; body->add_area(area); } @@ -74,6 +75,7 @@ bool GodotAreaPair2D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; body->remove_area(area); } @@ -103,7 +105,8 @@ GodotAreaPair2D::GodotAreaPair2D(GodotBody2D *p_body, int p_body_shape, GodotAre GodotAreaPair2D::~GodotAreaPair2D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; body->remove_area(area); } if (area->has_monitor_callback()) { diff --git a/servers/physics_2d/godot_area_pair_2d.h b/servers/physics_2d/godot_area_pair_2d.h index 938b9813b4c..eb091288a90 100644 --- a/servers/physics_2d/godot_area_pair_2d.h +++ b/servers/physics_2d/godot_area_pair_2d.h @@ -43,6 +43,7 @@ class GodotAreaPair2D : public GodotConstraint2D { bool colliding = false; bool has_space_override = false; bool process_collision = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/physics_3d/godot_area_pair_3d.cpp b/servers/physics_3d/godot_area_pair_3d.cpp index 5561210f2d3..aaa96f5a289 100644 --- a/servers/physics_3d/godot_area_pair_3d.cpp +++ b/servers/physics_3d/godot_area_pair_3d.cpp @@ -67,6 +67,7 @@ bool GodotAreaPair3D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; body->add_area(area); } @@ -75,6 +76,7 @@ bool GodotAreaPair3D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; body->remove_area(area); } @@ -104,7 +106,8 @@ GodotAreaPair3D::GodotAreaPair3D(GodotBody3D *p_body, int p_body_shape, GodotAre GodotAreaPair3D::~GodotAreaPair3D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; body->remove_area(area); } if (area->has_monitor_callback()) { @@ -242,6 +245,7 @@ bool GodotAreaSoftBodyPair3D::pre_solve(real_t p_step) { if (colliding) { if (has_space_override) { + body_has_attached_area = true; soft_body->add_area(area); } @@ -250,6 +254,7 @@ bool GodotAreaSoftBodyPair3D::pre_solve(real_t p_step) { } } else { if (has_space_override) { + body_has_attached_area = false; soft_body->remove_area(area); } @@ -276,7 +281,8 @@ GodotAreaSoftBodyPair3D::GodotAreaSoftBodyPair3D(GodotSoftBody3D *p_soft_body, i GodotAreaSoftBodyPair3D::~GodotAreaSoftBodyPair3D() { if (colliding) { - if (has_space_override) { + if (body_has_attached_area) { + body_has_attached_area = false; soft_body->remove_area(area); } if (area->has_monitor_callback()) { diff --git a/servers/physics_3d/godot_area_pair_3d.h b/servers/physics_3d/godot_area_pair_3d.h index c11fa078275..a2c5df0f7ad 100644 --- a/servers/physics_3d/godot_area_pair_3d.h +++ b/servers/physics_3d/godot_area_pair_3d.h @@ -44,6 +44,7 @@ class GodotAreaPair3D : public GodotConstraint3D { bool colliding = false; bool process_collision = false; bool has_space_override = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; @@ -83,6 +84,7 @@ class GodotAreaSoftBodyPair3D : public GodotConstraint3D { bool colliding = false; bool process_collision = false; bool has_space_override = false; + bool body_has_attached_area = false; public: virtual bool setup(real_t p_step) override; diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index 2cb7f32cc30..de4600395ba 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -2030,6 +2030,12 @@ void RendererCanvasCull::update_visibility_notifiers() { } } +Rect2 RendererCanvasCull::_debug_canvas_item_get_rect(RID p_item) { + Item *canvas_item = canvas_item_owner.get_or_null(p_item); + ERR_FAIL_NULL_V(canvas_item, Rect2()); + return canvas_item->get_rect(); +} + bool RendererCanvasCull::free(RID p_rid) { if (canvas_owner.owns(p_rid)) { Canvas *canvas = canvas_owner.get_or_null(p_rid); diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index 4f11d2c7b12..fc165628238 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -319,6 +319,8 @@ public: void update_visibility_notifiers(); + Rect2 _debug_canvas_item_get_rect(RID p_item); + bool free(RID p_rid); RendererCanvasCull(); ~RendererCanvasCull(); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 6888af7af91..2401c1105f8 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -389,6 +389,7 @@ void MeshStorage::mesh_add_surface(RID p_mesh, const RS::SurfaceData &p_surface) s->aabb = p_surface.aabb; s->bone_aabbs = p_surface.bone_aabbs; //only really useful for returning them. + s->mesh_to_skeleton_xform = p_surface.mesh_to_skeleton_xform; if (mesh->blend_shape_count > 0) { s->blend_shape_buffer = RD::get_singleton()->storage_buffer_create(p_surface.blend_shape_data.size(), p_surface.blend_shape_data); @@ -569,6 +570,7 @@ RS::SurfaceData MeshStorage::mesh_get_surface(RID p_mesh, int p_surface) const { } sd.bone_aabbs = s.bone_aabbs; + sd.mesh_to_skeleton_xform = s.mesh_to_skeleton_xform; if (s.blend_shape_buffer.is_valid()) { sd.blend_shape_data = RD::get_singleton()->buffer_get_data(s.blend_shape_buffer); @@ -615,15 +617,16 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { for (uint32_t i = 0; i < mesh->surface_count; i++) { AABB laabb; - if ((mesh->surfaces[i]->format & RS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) { - int bs = mesh->surfaces[i]->bone_aabbs.size(); - const AABB *skbones = mesh->surfaces[i]->bone_aabbs.ptr(); + const Mesh::Surface &surface = *mesh->surfaces[i]; + if ((surface.format & RS::ARRAY_FORMAT_BONES) && surface.bone_aabbs.size()) { + int bs = surface.bone_aabbs.size(); + const AABB *skbones = surface.bone_aabbs.ptr(); int sbs = skeleton->size; ERR_CONTINUE(bs > sbs); const float *baseptr = skeleton->data.ptr(); - bool first = true; + bool found_bone_aabb = false; if (skeleton->use_2d) { for (int j = 0; j < bs; j++) { @@ -643,11 +646,13 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[1][1] = dataptr[5]; mtx.origin.y = dataptr[7]; - AABB baabb = mtx.xform(skbones[j]); + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); - if (first) { + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } @@ -675,21 +680,29 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mtx.basis.rows[2][2] = dataptr[10]; mtx.origin.z = dataptr[11]; - AABB baabb = mtx.xform(skbones[j]); - if (first) { + // Transform bounds to skeleton's space before applying animation data. + AABB baabb = surface.mesh_to_skeleton_xform.xform(skbones[j]); + baabb = mtx.xform(baabb); + + if (!found_bone_aabb) { laabb = baabb; - first = false; + found_bone_aabb = true; } else { laabb.merge_with(baabb); } } } + if (found_bone_aabb) { + // Transform skeleton bounds back to mesh's space if any animated AABB applied. + laabb = surface.mesh_to_skeleton_xform.affine_inverse().xform(laabb); + } + if (laabb.size == Vector3()) { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } } else { - laabb = mesh->surfaces[i]->aabb; + laabb = surface.aabb; } if (i == 0) { diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index c23a5b1449c..4e064897643 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -116,6 +116,10 @@ private: Vector bone_aabbs; + // Transform used in runtime bone AABBs compute. + // As bone AABBs are saved in Mesh space, but bones animation is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + RID blend_shape_buffer; RID material; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 340eb7394b2..e6549f3b765 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -925,6 +925,8 @@ public: FUNC1(canvas_set_shadow_texture_size, int) + FUNC1R(Rect2, _debug_canvas_item_get_rect, RID) + /* GLOBAL SHADER UNIFORMS */ #undef server_name diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 266e3292adb..51687e2e2e3 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1410,6 +1410,14 @@ Array RenderingServer::_mesh_surface_get_skeleton_aabb_bind(RID p_mesh, int p_su } #endif +Rect2 RenderingServer::debug_canvas_item_get_rect(RID p_item) { +#ifdef TOOLS_ENABLED + return _debug_canvas_item_get_rect(p_item); +#else + return Rect2(); +#endif +} + int RenderingServer::global_shader_uniform_type_get_shader_datatype(GlobalShaderParameterType p_type) { switch (p_type) { case RS::GLOBAL_VAR_TYPE_BOOL: @@ -2636,6 +2644,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("canvas_item_set_visibility_notifier", "item", "enable", "area", "enter_callable", "exit_callable"), &RenderingServer::canvas_item_set_visibility_notifier); ClassDB::bind_method(D_METHOD("canvas_item_set_canvas_group_mode", "item", "mode", "clear_margin", "fit_empty", "fit_margin", "blur_mipmaps"), &RenderingServer::canvas_item_set_canvas_group_mode, DEFVAL(5.0), DEFVAL(false), DEFVAL(0.0), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("debug_canvas_item_get_rect", "item"), &RenderingServer::debug_canvas_item_get_rect); + BIND_ENUM_CONSTANT(NINE_PATCH_STRETCH); BIND_ENUM_CONSTANT(NINE_PATCH_TILE); BIND_ENUM_CONSTANT(NINE_PATCH_TILE_FIT); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index deac2a59f92..d391ffe99bc 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -312,6 +312,10 @@ public: Vector lods; Vector bone_aabbs; + // Transforms used in runtime bone AABBs compute. + // Since bone AABBs is saved in Mesh space, but bones is in Skeleton space. + Transform3D mesh_to_skeleton_xform; + Vector blend_shape_data; RID material; @@ -1461,6 +1465,9 @@ public: virtual void canvas_set_shadow_texture_size(int p_size) = 0; + Rect2 debug_canvas_item_get_rect(RID p_item); + virtual Rect2 _debug_canvas_item_get_rect(RID p_item) = 0; + /* GLOBAL SHADER UNIFORMS */ enum GlobalShaderParameterType { diff --git a/tests/core/io/test_image.h b/tests/core/io/test_image.h index 4f43f184523..d88bcaa813a 100644 --- a/tests/core/io/test_image.h +++ b/tests/core/io/test_image.h @@ -323,6 +323,30 @@ TEST_CASE("[Image] Modifying pixels of an image") { CHECK_MESSAGE(gray_image->get_pixel(2, 2).is_equal_approx(Color(0.266666681, 0.266666681, 0.266666681, 1)), "convert() RGBA to L8 should be around 0.266666681 (68)."); } } + +TEST_CASE("[Image] Convert image") { + for (int format = Image::FORMAT_RF; format < Image::FORMAT_RGBE9995; format++) { + for (int new_format = Image::FORMAT_RF; new_format < Image::FORMAT_RGBE9995; new_format++) { + Ref image = memnew(Image(4, 4, false, (Image::Format)format)); + image->convert((Image::Format)new_format); + String format_string = Image::format_names[(Image::Format)format]; + String new_format_string = Image::format_names[(Image::Format)new_format]; + format_string = "Error converting from " + format_string + " to " + new_format_string + "."; + CHECK_MESSAGE(image->get_format() == new_format, format_string); + } + } + + Ref image = memnew(Image(4, 4, false, Image::FORMAT_RGBA8)); + PackedByteArray image_data = image->get_data(); + image->convert((Image::Format)-1); + CHECK_MESSAGE(image->get_data() == image_data, "Image conversion to invalid type (-1) should not alter image."); + + Ref image2 = memnew(Image(4, 4, false, Image::FORMAT_RGBA8)); + image_data = image2->get_data(); + image2->convert((Image::Format)(Image::FORMAT_MAX + 1)); + CHECK_MESSAGE(image2->get_data() == image_data, "Image conversion to invalid type (Image::FORMAT_MAX + 1) should not alter image."); +} + } // namespace TestImage #endif // TEST_IMAGE_H diff --git a/thirdparty/README.md b/thirdparty/README.md index a751452d0d4..c3038b453ac 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -441,7 +441,7 @@ that file when upgrading. ## minizip - Upstream: https://www.zlib.net -- Version: 1.3 (zlib contrib, 2023) +- Version: 1.3.1 (zlib contrib, 2024) - License: zlib Files extracted from the upstream source: @@ -814,7 +814,7 @@ Files extracted from upstream source: ## zlib - Upstream: https://www.zlib.net -- Version: 1.3 (2023) +- Version: 1.3.1 (2024) - License: zlib Files extracted from upstream source: diff --git a/thirdparty/minizip/ioapi.h b/thirdparty/minizip/ioapi.h index 14266141c6f..509b52da8af 100644 --- a/thirdparty/minizip/ioapi.h +++ b/thirdparty/minizip/ioapi.h @@ -144,7 +144,7 @@ typedef long (ZCALLBACK *tell_file_func) (voidpf opaque, voidpf stream) typedef long (ZCALLBACK *seek_file_func) (voidpf opaque, voidpf stream, uLong offset, int origin); -/* here is the "old" 32 bits structure structure */ +/* here is the "old" 32 bits structure */ typedef struct zlib_filefunc_def_s { open_file_func zopen_file; diff --git a/thirdparty/minizip/patches/godot-seek.patch b/thirdparty/minizip/patches/godot-seek.patch index 269536ba855..649de2fe2fb 100644 --- a/thirdparty/minizip/patches/godot-seek.patch +++ b/thirdparty/minizip/patches/godot-seek.patch @@ -26,7 +26,7 @@ index 782d32469a..2e89f5f41a 100644 +*/ +/* GODOT end */ diff --git a/thirdparty/minizip/ioapi.h b/thirdparty/minizip/ioapi.h -index c588a18d03..14266141c6 100644 +index a2d2e6e60d..509b52da8a 100644 --- a/thirdparty/minizip/ioapi.h +++ b/thirdparty/minizip/ioapi.h @@ -155,6 +155,10 @@ typedef struct zlib_filefunc_def_s @@ -52,7 +52,7 @@ index c588a18d03..14266141c6 100644 void fill_fopen64_filefunc(zlib_filefunc64_def* pzlib_filefunc_def); diff --git a/thirdparty/minizip/unzip.c b/thirdparty/minizip/unzip.c -index ed763f89f1..2f8c5f3675 100644 +index ea05b7d62a..981ba3c0cb 100644 --- a/thirdparty/minizip/unzip.c +++ b/thirdparty/minizip/unzip.c @@ -152,6 +152,9 @@ typedef struct @@ -232,7 +232,7 @@ index ed763f89f1..2f8c5f3675 100644 return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); } diff --git a/thirdparty/minizip/unzip.h b/thirdparty/minizip/unzip.h -index 14105840f6..1a477ecb50 100644 +index 5cfc9c6274..0639674574 100644 --- a/thirdparty/minizip/unzip.h +++ b/thirdparty/minizip/unzip.h @@ -202,6 +202,10 @@ extern int ZEXPORT unzClose(unzFile file); @@ -261,7 +261,7 @@ index 14105840f6..1a477ecb50 100644 extern ZPOS64_T ZEXPORT unztell64(unzFile file); diff --git a/thirdparty/minizip/zip.c b/thirdparty/minizip/zip.c -index 3d3d4cadde..e859f9e42f 100644 +index 60bdffac34..1d2d918e72 100644 --- a/thirdparty/minizip/zip.c +++ b/thirdparty/minizip/zip.c @@ -820,9 +820,11 @@ extern zipFile ZEXPORT zipOpen3(const void *pathname, int append, zipcharpc* glo @@ -279,7 +279,7 @@ index 3d3d4cadde..e859f9e42f 100644 ziinit.z_filefunc = *pzlib_filefunc64_32_def; ziinit.filestream = ZOPEN64(ziinit.z_filefunc, -@@ -1171,8 +1173,10 @@ extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, c +@@ -1182,8 +1184,10 @@ extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, c { if(zi->ci.method == Z_DEFLATED) { diff --git a/thirdparty/minizip/unzip.c b/thirdparty/minizip/unzip.c index 41a203b164c..981ba3c0cb7 100644 --- a/thirdparty/minizip/unzip.c +++ b/thirdparty/minizip/unzip.c @@ -117,7 +117,7 @@ const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; -/* unz_file_info_interntal contain internal info about a file in zipfile*/ +/* unz_file_info64_internal contain internal info about a file in zipfile*/ typedef struct unz_file_info64_internal_s { ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ @@ -453,7 +453,7 @@ local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; - /* number of the disk with the start of the zip64 end of central directory */ + /* number of the disk with the start of the zip64 end of central directory */ if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) return CENTRALDIRINVALID; if (uL != 0) @@ -500,9 +500,9 @@ local unzFile unzOpenInternal(const void *path, ZPOS64_T central_pos; uLong uL; - uLong number_disk; /* number of the current dist, used for + uLong number_disk; /* number of the current disk, used for spanning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used + uLong number_disk_with_CD; /* number the disk with central dir, used for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry_CD; /* total number of entries in the central dir diff --git a/thirdparty/minizip/unzip.h b/thirdparty/minizip/unzip.h index 1a477ecb507..06396745744 100644 --- a/thirdparty/minizip/unzip.h +++ b/thirdparty/minizip/unzip.h @@ -310,7 +310,7 @@ extern int ZEXPORT unzGetCurrentFileInfo(unzFile file, Get Info about the current file if pfile_info!=NULL, the *pfile_info structure will contain some info about the current file - if szFileName!=NULL, the filemane string will be copied in szFileName + if szFileName!=NULL, the filename string will be copied in szFileName (fileNameBufferSize is the size of the buffer) if extraField!=NULL, the extra field information will be copied in extraField (extraFieldBufferSize is the size of the buffer). diff --git a/thirdparty/minizip/zip.c b/thirdparty/minizip/zip.c index e859f9e42f1..4440f6d9411 100644 --- a/thirdparty/minizip/zip.c +++ b/thirdparty/minizip/zip.c @@ -575,7 +575,7 @@ local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; - /* number of the disk with the start of the zip64 end of central directory */ + /* number of the disk with the start of the zip64 end of central directory */ if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) return 0; if (uL != 0) @@ -614,9 +614,9 @@ local int LoadCentralDirectoryRecord(zip64_internal* pziinit) { ZPOS64_T central_pos; uLong uL; - uLong number_disk; /* number of the current dist, used for + uLong number_disk; /* number of the current disk, used for spanning ZIP, unsupported, always 0*/ - uLong number_disk_with_CD; /* number the the disk with central dir, used + uLong number_disk_with_CD; /* number of the disk with central dir, used for spanning ZIP, unsupported, always 0*/ ZPOS64_T number_entry; ZPOS64_T number_entry_CD; /* total number of entries in @@ -1601,7 +1601,7 @@ extern int ZEXPORT zipCloseFileInZipRaw64(zipFile file, ZPOS64_T uncompressed_si if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) { - // we can not write more data to the buffer that we have room for. + // we cannot write more data to the buffer that we have room for. return ZIP_BADZIPFILE; } @@ -1865,7 +1865,7 @@ extern int ZEXPORT zipClose(zipFile file, const char* global_comment) { free_linkedlist(&(zi->central_dir)); pos = centraldir_pos_inzip - zi->add_position_when_writing_offset; - if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + if(pos >= 0xffffffff || zi->number_entry >= 0xFFFF) { ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); diff --git a/thirdparty/minizip/zip.h b/thirdparty/minizip/zip.h index 5fc08413241..3e230d3405f 100644 --- a/thirdparty/minizip/zip.h +++ b/thirdparty/minizip/zip.h @@ -177,9 +177,9 @@ extern int ZEXPORT zipOpenNewFileInZip64(zipFile file, filename : the filename in zip (if NULL, '-' without quote will be used *zipfi contain supplemental information if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local - contains the extrafield data the the local header + contains the extrafield data for the local header if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global - contains the extrafield data the the local header + contains the extrafield data for the global header if comment != NULL, comment contain the comment string method contain the compression method (0 for store, Z_DEFLATED for deflate) level contain the level of compression (can be Z_DEFAULT_COMPRESSION) diff --git a/thirdparty/zlib/deflate.c b/thirdparty/zlib/deflate.c index bd011751920..012ea8148e8 100644 --- a/thirdparty/zlib/deflate.c +++ b/thirdparty/zlib/deflate.c @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.3 Copyright 1995-2023 Jean-loup Gailly and Mark Adler "; + " deflate 1.3.1 Copyright 1995-2024 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -493,7 +493,7 @@ int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, * symbols from which it is being constructed. */ - s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, 4); + s->pending_buf = (uchf *) ZALLOC(strm, s->lit_bufsize, LIT_BUFS); s->pending_buf_size = (ulg)s->lit_bufsize * 4; if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || @@ -503,8 +503,14 @@ int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, deflateEnd (strm); return Z_MEM_ERROR; } +#ifdef LIT_MEM + s->d_buf = (ushf *)(s->pending_buf + (s->lit_bufsize << 1)); + s->l_buf = s->pending_buf + (s->lit_bufsize << 2); + s->sym_end = s->lit_bufsize - 1; +#else s->sym_buf = s->pending_buf + s->lit_bufsize; s->sym_end = (s->lit_bufsize - 1) * 3; +#endif /* We avoid equality with lit_bufsize*3 because of wraparound at 64K * on 16 bit machines and because stored blocks are restricted to * 64K-1 bytes. @@ -720,9 +726,15 @@ int ZEXPORT deflatePrime(z_streamp strm, int bits, int value) { if (deflateStateCheck(strm)) return Z_STREAM_ERROR; s = strm->state; +#ifdef LIT_MEM + if (bits < 0 || bits > 16 || + (uchf *)s->d_buf < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; +#else if (bits < 0 || bits > 16 || s->sym_buf < s->pending_out + ((Buf_size + 7) >> 3)) return Z_BUF_ERROR; +#endif do { put = Buf_size - s->bi_valid; if (put > bits) @@ -1294,7 +1306,7 @@ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); - ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, 4); + ds->pending_buf = (uchf *) ZALLOC(dest, ds->lit_bufsize, LIT_BUFS); if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || ds->pending_buf == Z_NULL) { @@ -1305,10 +1317,15 @@ int ZEXPORT deflateCopy(z_streamp dest, z_streamp source) { zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); - zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + zmemcpy(ds->pending_buf, ss->pending_buf, ds->lit_bufsize * LIT_BUFS); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); +#ifdef LIT_MEM + ds->d_buf = (ushf *)(ds->pending_buf + (ds->lit_bufsize << 1)); + ds->l_buf = ds->pending_buf + (ds->lit_bufsize << 2); +#else ds->sym_buf = ds->pending_buf + ds->lit_bufsize; +#endif ds->l_desc.dyn_tree = ds->dyn_ltree; ds->d_desc.dyn_tree = ds->dyn_dtree; @@ -1539,13 +1556,21 @@ local uInt longest_match(deflate_state *s, IPos cur_match) { */ local void check_match(deflate_state *s, IPos start, IPos match, int length) { /* check that the match is indeed a match */ - if (zmemcmp(s->window + match, - s->window + start, length) != EQUAL) { - fprintf(stderr, " start %u, match %u, length %d\n", - start, match, length); + Bytef *back = s->window + (int)match, *here = s->window + start; + IPos len = length; + if (match == (IPos)-1) { + /* match starts one byte before the current window -- just compare the + subsequent length-1 bytes */ + back++; + here++; + len--; + } + if (zmemcmp(back, here, len) != EQUAL) { + fprintf(stderr, " start %u, match %d, length %d\n", + start, (int)match, length); do { - fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); - } while (--length != 0); + fprintf(stderr, "(%02x %02x)", *back++, *here++); + } while (--len != 0); z_error("invalid match"); } if (z_verbose > 1) { diff --git a/thirdparty/zlib/deflate.h b/thirdparty/zlib/deflate.h index 8696791429f..300c6ada62b 100644 --- a/thirdparty/zlib/deflate.h +++ b/thirdparty/zlib/deflate.h @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2018 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -23,6 +23,10 @@ # define GZIP #endif +/* define LIT_MEM to slightly increase the speed of deflate (order 1% to 2%) at + the cost of a larger memory footprint */ +/* #define LIT_MEM */ + /* =========================================================================== * Internal compression state. */ @@ -217,7 +221,14 @@ typedef struct internal_state { /* Depth of each subtree used as tie breaker for trees of equal frequency */ +#ifdef LIT_MEM +# define LIT_BUFS 5 + ushf *d_buf; /* buffer for distances */ + uchf *l_buf; /* buffer for literals/lengths */ +#else +# define LIT_BUFS 4 uchf *sym_buf; /* buffer for distances and literals/lengths */ +#endif uInt lit_bufsize; /* Size of match buffer for literals/lengths. There are 4 reasons for @@ -239,7 +250,7 @@ typedef struct internal_state { * - I can't count above 4 */ - uInt sym_next; /* running index in sym_buf */ + uInt sym_next; /* running index in symbol buffer */ uInt sym_end; /* symbol table full when sym_next reaches this */ ulg opt_len; /* bit length of current block with optimal trees */ @@ -318,6 +329,25 @@ void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, extern const uch ZLIB_INTERNAL _dist_code[]; #endif +#ifdef LIT_MEM +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->sym_next] = 0; \ + s->l_buf[s->sym_next++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (uch)(length); \ + ush dist = (ush)(distance); \ + s->d_buf[s->sym_next] = dist; \ + s->l_buf[s->sym_next++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->sym_next == s->sym_end); \ + } +#else # define _tr_tally_lit(s, c, flush) \ { uch cc = (c); \ s->sym_buf[s->sym_next++] = 0; \ @@ -337,6 +367,7 @@ void ZLIB_INTERNAL _tr_stored_block(deflate_state *s, charf *buf, s->dyn_dtree[d_code(dist)].Freq++; \ flush = (s->sym_next == s->sym_end); \ } +#endif #else # define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) # define _tr_tally_dist(s, distance, length, flush) \ diff --git a/thirdparty/zlib/gzguts.h b/thirdparty/zlib/gzguts.h index f9375047e8c..eba72085bb7 100644 --- a/thirdparty/zlib/gzguts.h +++ b/thirdparty/zlib/gzguts.h @@ -1,5 +1,5 @@ /* gzguts.h -- zlib internal header definitions for gz* operations - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -210,9 +210,5 @@ char ZLIB_INTERNAL *gz_strwinerror(DWORD error); /* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t value -- needed when comparing unsigned to z_off64_t, which is signed (possible z_off64_t types off_t, off64_t, and long are all signed) */ -#ifdef INT_MAX -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) -#else unsigned ZLIB_INTERNAL gz_intmax(void); -# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) -#endif +#define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) diff --git a/thirdparty/zlib/gzlib.c b/thirdparty/zlib/gzlib.c index 29fc4486fba..983153cc8e4 100644 --- a/thirdparty/zlib/gzlib.c +++ b/thirdparty/zlib/gzlib.c @@ -1,5 +1,5 @@ /* gzlib.c -- zlib functions common to reading and writing gzip files - * Copyright (C) 2004-2019 Mark Adler + * Copyright (C) 2004-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -563,20 +563,20 @@ void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) { #endif } -#ifndef INT_MAX /* portably return maximum value for an int (when limits.h presumed not available) -- we need to do this to cover cases where 2's complement not used, since C standard permits 1's complement and sign-bit representations, otherwise we could just use ((unsigned)-1) >> 1 */ unsigned ZLIB_INTERNAL gz_intmax(void) { - unsigned p, q; - - p = 1; +#ifdef INT_MAX + return INT_MAX; +#else + unsigned p = 1, q; do { q = p; p <<= 1; p++; } while (p > q); return q >> 1; -} #endif +} diff --git a/thirdparty/zlib/inflate.c b/thirdparty/zlib/inflate.c index b0757a9b249..94ecff015a9 100644 --- a/thirdparty/zlib/inflate.c +++ b/thirdparty/zlib/inflate.c @@ -1387,7 +1387,7 @@ int ZEXPORT inflateSync(z_streamp strm) { /* if first time, start search in bit buffer */ if (state->mode != SYNC) { state->mode = SYNC; - state->hold <<= state->bits & 7; + state->hold >>= state->bits & 7; state->bits -= state->bits & 7; len = 0; while (state->bits >= 8) { diff --git a/thirdparty/zlib/inftrees.c b/thirdparty/zlib/inftrees.c index 8a208c2daa8..98cfe164458 100644 --- a/thirdparty/zlib/inftrees.c +++ b/thirdparty/zlib/inftrees.c @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2023 Mark Adler + * Copyright (C) 1995-2024 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.3 Copyright 1995-2023 Mark Adler "; + " inflate 1.3.1 Copyright 1995-2024 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -57,7 +57,7 @@ int ZLIB_INTERNAL inflate_table(codetype type, unsigned short FAR *lens, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 198, 203}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 203, 77}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, diff --git a/thirdparty/zlib/inftrees.h b/thirdparty/zlib/inftrees.h index a10712d8cb5..396f74b5da7 100644 --- a/thirdparty/zlib/inftrees.h +++ b/thirdparty/zlib/inftrees.h @@ -41,8 +41,8 @@ typedef struct { examples/enough.c found in the zlib distribution. The arguments to that program are the number of symbols, the initial root table size, and the maximum bit length of a code. "enough 286 9 15" for literal/length codes - returns returns 852, and "enough 30 6 15" for distance codes returns 592. - The initial root table size (9 or 6) is found in the fifth argument of the + returns 852, and "enough 30 6 15" for distance codes returns 592. The + initial root table size (9 or 6) is found in the fifth argument of the inflate_table() calls in inflate.c and infback.c. If the root table size is changed, then these maximum sizes would be need to be recalculated and updated. */ diff --git a/thirdparty/zlib/trees.c b/thirdparty/zlib/trees.c index 8dbdc40bacc..6a523ef34e3 100644 --- a/thirdparty/zlib/trees.c +++ b/thirdparty/zlib/trees.c @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2021 Jean-loup Gailly + * Copyright (C) 1995-2024 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -899,14 +899,19 @@ local void compress_block(deflate_state *s, const ct_data *ltree, const ct_data *dtree) { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ - unsigned sx = 0; /* running index in sym_buf */ + unsigned sx = 0; /* running index in symbol buffers */ unsigned code; /* the code to send */ int extra; /* number of extra bits to send */ if (s->sym_next != 0) do { +#ifdef LIT_MEM + dist = s->d_buf[sx]; + lc = s->l_buf[sx++]; +#else dist = s->sym_buf[sx++] & 0xff; dist += (unsigned)(s->sym_buf[sx++] & 0xff) << 8; lc = s->sym_buf[sx++]; +#endif if (dist == 0) { send_code(s, lc, ltree); /* send a literal byte */ Tracecv(isgraph(lc), (stderr," '%c' ", lc)); @@ -931,8 +936,12 @@ local void compress_block(deflate_state *s, const ct_data *ltree, } } /* literal or match pair ? */ - /* Check that the overlay between pending_buf and sym_buf is ok: */ + /* Check for no overlay of pending_buf on needed symbols */ +#ifdef LIT_MEM + Assert(s->pending < 2 * (s->lit_bufsize + sx), "pendingBuf overflow"); +#else Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow"); +#endif } while (sx < s->sym_next); @@ -1082,9 +1091,14 @@ void ZLIB_INTERNAL _tr_flush_block(deflate_state *s, charf *buf, * the current block must be flushed. */ int ZLIB_INTERNAL _tr_tally(deflate_state *s, unsigned dist, unsigned lc) { +#ifdef LIT_MEM + s->d_buf[s->sym_next] = (ush)dist; + s->l_buf[s->sym_next++] = (uch)lc; +#else s->sym_buf[s->sym_next++] = (uch)dist; s->sym_buf[s->sym_next++] = (uch)(dist >> 8); s->sym_buf[s->sym_next++] = (uch)lc; +#endif if (dist == 0) { /* lc is the unmatched char */ s->dyn_ltree[lc].Freq++; diff --git a/thirdparty/zlib/zconf.h b/thirdparty/zlib/zconf.h index fb76ffe312a..62adc8d8431 100644 --- a/thirdparty/zlib/zconf.h +++ b/thirdparty/zlib/zconf.h @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -300,14 +300,6 @@ # endif #endif -#ifndef Z_ARG /* function prototypes for stdarg */ -# if defined(STDC) || defined(Z_HAVE_STDARG_H) -# define Z_ARG(args) args -# else -# define Z_ARG(args) () -# endif -#endif - /* The following definitions for FAR are needed only for MSDOS mixed * model programming (small or medium model with some far allocations). * This was tested only with MSC; for other MSDOS compilers you may have diff --git a/thirdparty/zlib/zlib.h b/thirdparty/zlib/zlib.h index 6b7244f9943..8d4b932eaf6 100644 --- a/thirdparty/zlib/zlib.h +++ b/thirdparty/zlib/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.3, August 18th, 2023 + version 1.3.1, January 22nd, 2024 - Copyright (C) 1995-2023 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.3" -#define ZLIB_VERNUM 0x1300 +#define ZLIB_VERSION "1.3.1" +#define ZLIB_VERNUM 0x1310 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 3 -#define ZLIB_VER_REVISION 0 +#define ZLIB_VER_REVISION 1 #define ZLIB_VER_SUBREVISION 0 /* @@ -936,10 +936,10 @@ ZEXTERN int ZEXPORT inflateSync(z_streamp strm); inflateSync returns Z_OK if a possible full flush point has been found, Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. - In the success case, the application may save the current current value of - total_in which indicates where valid compressed data was found. In the - error case, the application may repeatedly call inflateSync, providing more - input each time, until success or end of the input data. + In the success case, the application may save the current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy(z_streamp dest, @@ -1758,14 +1758,14 @@ ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. + len2. len2 must be non-negative. */ /* ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with - crc32_combine_op(). + crc32_combine_op(). len2 must be non-negative. */ ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); diff --git a/thirdparty/zlib/zutil.h b/thirdparty/zlib/zutil.h index 902a304cc2d..48dd7febae6 100644 --- a/thirdparty/zlib/zutil.h +++ b/thirdparty/zlib/zutil.h @@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2022 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -56,7 +56,7 @@ typedef unsigned long ulg; extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ -#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] +#define ERR_MSG(err) z_errmsg[(err) < -6 || (err) > 2 ? 9 : 2 - (err)] #define ERR_RETURN(strm,err) \ return (strm->msg = ERR_MSG(err), (err)) @@ -137,17 +137,8 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # endif #endif -#if defined(MACOS) || defined(TARGET_OS_MAC) +#if defined(MACOS) # define OS_CODE 7 -# ifndef Z_SOLO -# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include /* for fdopen */ -# else -# ifndef fdopen -# define fdopen(fd,mode) NULL /* No fdopen() */ -# endif -# endif -# endif #endif #ifdef __acorn @@ -170,18 +161,6 @@ extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ # define OS_CODE 19 #endif -#if defined(_BEOS_) || defined(RISCOS) -# define fdopen(fd,mode) NULL /* No fdopen() */ -#endif - -#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX -# if defined(_WIN32_WCE) -# define fdopen(fd,mode) NULL /* No fdopen() */ -# else -# define fdopen(fd,type) _fdopen(fd,type) -# endif -#endif - #if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008