summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md422
-rw-r--r--DONORS.md2
-rw-r--r--SConstruct5
-rw-r--r--core/doc_data.h31
-rw-r--r--doc/classes/SpriteBase3D.xml3
-rw-r--r--doc/classes/TabContainer.xml3
-rw-r--r--doc/classes/Transform2D.xml12
-rw-r--r--doc/classes/Transform3D.xml14
-rw-r--r--doc/classes/Vector2.xml14
-rw-r--r--doc/classes/Vector3.xml20
-rw-r--r--doc/classes/Viewport.xml4
-rw-r--r--doc/classes/float.xml18
-rw-r--r--doc/classes/int.xml28
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp4
-rw-r--r--editor/code_editor.cpp2
-rw-r--r--editor/editor_data.cpp7
-rw-r--r--editor/localization_editor.cpp6
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp6
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp22
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp2
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp5
-rw-r--r--editor/project_manager.cpp2
-rw-r--r--editor/scene_tree_dock.cpp1
-rw-r--r--main/SCsub13
-rw-r--r--main/main.cpp4
-rw-r--r--main/splash_editor.pngbin37471 -> 0 bytes
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp28
-rw-r--r--modules/minimp3/audio_stream_mp3.h2
-rw-r--r--platform/javascript/js/libs/library_godot_input.js6
-rw-r--r--scene/2d/camera_2d.cpp2
-rw-r--r--scene/2d/gpu_particles_2d.cpp12
-rw-r--r--scene/2d/gpu_particles_2d.h9
-rw-r--r--scene/2d/path_2d.cpp14
-rw-r--r--scene/2d/path_2d.h1
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/debugger/scene_debugger.cpp2
-rw-r--r--scene/gui/tab_bar.cpp16
-rw-r--r--scene/gui/texture_progress_bar.cpp1
-rw-r--r--scene/main/viewport.cpp11
-rw-r--r--scene/resources/visual_shader_particle_nodes.cpp157
-rw-r--r--scene/resources/visual_shader_particle_nodes.h1
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp2
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.cpp3
-rw-r--r--servers/rendering/shader_language.cpp124
-rw-r--r--servers/rendering/shader_language.h3
-rw-r--r--servers/rendering/shader_types.cpp32
-rw-r--r--servers/rendering/shader_warnings.cpp4
-rw-r--r--servers/rendering/shader_warnings.h2
-rw-r--r--tests/servers/test_shader_lang.cpp2
-rw-r--r--thirdparty/README.md13
-rw-r--r--thirdparty/minimp3/minimp3.h24
-rw-r--r--thirdparty/minimp3/minimp3_ex.h7
55 files changed, 861 insertions, 275 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe5631cefd..d1e622edb4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,428 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+## [4.0] - TBD
+
+### Added
+
+#### Animation
+
+- [Revamped 3D animation storage.](https://godotengine.org/article/animation-data-redesign-40)
+ - [New blend shape track to adjust blend shapes in animations more efficiently.](https://github.com/godotengine/godot/pull/53865)
+ - [New expression-based transitions in AnimationTree state machines.](https://github.com/godotengine/godot/pull/54327)
+ - [Support for animation compression to improve performance with long animations such as cutscenes.](https://github.com/godotengine/godot/pull/54050)
+ - [Replaced transform tracks by position, rotation and scale tracks.](https://github.com/godotengine/godot/pull/53689)
+ - [Removed the animation dependency on bone rests.](https://github.com/godotengine/godot/pull/53765)
+ - Better compatibility with models that use non-uniform scaling in animations.
+ - Better compatibility with models exported from Maya and 3DS Max.
+ - Easier animation reuse across different models.
+ - Easier procedural generation of animations.
+
+#### Core
+
+- New TileMap and TileSet resources.
+- New Vector2i, Vector3i and Rect2i types.
+ - These are integer variants of Vector2, Vector3 and Rect2.
+- Callable type for first-class functions (can be created with lambdas in GDScript).
+- The Euler rotation order can now be adjusted in Node3D.
+- [New `Array.map()`, `Array.filter()` and `Array.reduce()` methods that can be used with Callables.](https://github.com/godotengine/godot/pull/38645)
+- [Rewritten Tween with more functionality](https://github.com/godotengine/godot/pull/41794).
+ - Tween is no longer a node.
+ - Easier chaining of tweens.
+ - Low-level tweening option to get an interpolated value directly.
+ - *Existing projects will have to be modified to account for this, as automatic conversion isn't feasible.*
+- [New and improved IK in Skeleton2D](https://github.com/godotengine/godot/pull/40347).
+ - New classes: SkeletonModifier2D, SkeletonModifierStack2D, SkeletonModification2DLookAt, SkeletonModification2DCCDIK, SkeletonModification2DFABRIK, SkeletonModification2DJiggle, SkeletonModification2DTwoBoneIK, PhysicalBone2D, SkeletonModification2DPhysicalBones, SkeletonModification2DStackHolder.
+ - New `Transform2D.looking_at()` function.
+- [New and improved IK in Skeleton3D](https://github.com/godotengine/godot/pull/39353).
+ - New classes: SkeletonModifier3D, SkeletonModifierStack3D, SkeletonModifier3DLookAt, SkeletonModification3DCCDIK, SkeletonModification3DFABRIK, SkeletonModification3DJiggle, SkeletonModification3DTwoBoneIK, SkeletonModification3DStackHolder.
+ - The Bone struct now includes a local_pose_override.
+ - The Bone struct now keeps track of its children bones, if it has any.
+ - Added functions to Skeleton3D for getting the forward vector using the information stored in the rest pose for the bones.
+ - New `Basis.rotate_to_align()` function.
+ - Refactored the BoneAttachment3D node.
+ - Removed the `process_list` functions.
+- [New GradientTexture2D resource (useful for 2D lights, particles, …)](https://github.com/godotengine/godot/pull/53234).
+- [Support for gettext PO template generation from scene and script files.](https://github.com/godotengine/godot/pull/39415)
+ - Translation parser plugins can be written to allow extracting strings from custom file types.
+- [New Time singleton to replace date/time handling methods in the OS singleton.](https://github.com/godotengine/godot/pull/49123)
+ - Includes new methods to handle ISO 8601 timestamp conversion.
+- [Support for custom performance monitors](https://github.com/godotengine/godot/pull/39302).
+- New `randi_range()` global scope function to return a random *integer* number within a range.
+- New `randfn()` global scope function to return a floating-point number along a normal gaussian distribution.
+- [New `pingpong()` global scope function to return a floating-point number that increments then decrements in a "sawtooth" fashion.](https://github.com/godotengine/godot/pull/46346)
+- Vectors and Colors can now be clamped between two values their respective `clamp()` methods.
+- New `Vector3.limit_length()` method as a Vector3 counterpart to `Vector2.limit_length()` (formerly `Vector2.clamped()`).
+- New PackedArrays to replace PoolArrays.
+ - 64-bit integer and float arrays are now available in addition to the existing 32-bit ones.
+- New `ConfigFile.parse(data: String)` method to load a string as if it was a ConfigFile on disk.
+- New `Image.save_png_to_buffer()` method to save a PNG image to memory as a PackedByteArray (instead of saving to disk).
+- [New File API to check, read and write symbolic links on macOS and Linux.](https://github.com/godotengine/godot/pull/46866)
+- [Replaced GDNative with GDExtension](https://godotengine.org/article/introducing-gd-extensions).
+ - Easier setup and compilation for various platforms.
+ - Code structure is more similar to [statically compiled C++ modules](https://docs.godotengine.org/en/stable/development/cpp/custom_modules_in_cpp.html).
+ - Lower performance overhead compared to GDNative.
+
+#### Editor
+
+- New TileMap and TileSet editors with better usability.
+- Support for multiple windows.
+ - Docks can be moved out of the main window into separate windows.
+ - Single-window mode can be enabled in the Editor Settings to revert to the old behavior.
+- Movement and scaling handles in the 2D editor (similar to the 3D editor).
+- New Replace in Files dialog in the script editor to complement Find in Files.
+- **macOS:** More built-in mouse cursors are now exposed (such as diagonal resize cursors).
+- **macOS:** Support for building Godot with Clang sanitizers.
+- **HTML5:** [Support for profiling projects exported to HTML5.](https://godotengine.org/article/html5-export-profiling)
+
+#### Export
+
+- **macOS:** Projects can now optionally be exported to a application bundle contained within a ZIP archive.
+ - Previously, a DMG image was always used when exporting from macOS.
+- **macOS:** DMG images can now be codesigned after exporting.
+
+#### GDScript
+
+- GDScript was rewritten from scratch with a cleaner approach.
+ - Annotations to replace keywords in certain cases (`@export`, `@onready`, `@rpc()`, `@tool`, `@warning_ignore()`, …).
+ - Typed arrays (`var array_of_nodes: Array[Node]`). Any type can be used, including custom classes.
+ - See individual progress reports for more information:
+ [#1](https://godotengine.org/article/gdscript-progress-report-writing-tokenizer),
+ [#2](https://godotengine.org/article/gdscript-progress-report-writing-new-parser),
+ [#3](https://godotengine.org/article/gdscript-progress-report-type-checking-back).
+- [New documentation generation system](https://github.com/godotengine/godot/pull/41095).
+ - Comments starting with `##` are considered documentation comments.
+ - Documentation comments be placed before any member variable, constant, enum or function declaration, or at the top of a file.
+ - Documentation comments appear in the editor help and when hovering exported properties in the inspector.
+
+#### GUI
+
+- Support for multiple windows on desktop platforms. Projects can spawn additional windows, each with their own viewport.
+ - Added `NOTIFICATION_APPLICATION_FOCUS_IN` and `NOTIFICATION_APPLICATION_FOCUS_OUT` notifications for "global" project focus changes (separate from `NOTIFICATION_WM_FOCUS_IN` and `NOTIFICATION_WM_FOCUS_OUT`).
+- RichTextLabel property `fit_content_height` to make the label's height fit its content automatically (not always reliable).
+- RichTextLabel's `img` tag now supports an optional `color` attribute to modulate the image.
+- `get_char_size()` is now exposed in Font, making it usable in DynamicFont rather than being limited to BitmapFont.
+- [Tree can now highlight relationship lines for the currently selected item, its parents and direct children.](https://github.com/godotengine/godot/pull/48546)
+ - This is used in the scene tree dock in the editor.
+
+#### Import
+
+- Support for importing lights from glTF scenes.
+
+#### Input
+
+- Support for physical (keyboard layout-independent) key codes.
+ - This can be used to provide <kbd>W</kbd>/<kbd>A</kbd>/<kbd>S</kbd>/<kbd>D</kbd> controls that work on any keyboard layout.
+- `DisplayServer.keyboard_get_current_layout()` and `DisplayServer.keyboard_get_layout_*()` methods to get information about keyboard layouts.
+- New `Input.MOUSE_MODE_CONFINED_HIDDEN` mouse mode to combine the confined and hidden mouse modes.
+
+#### Mono/C#
+
+- Support for exporting C# projects to iOS and HTML5.
+- [C# events can now be used to implement Godot signals](https://godotengine.org/article/csharp-ios-signals-events).
+- [New Visual Studio and Visual Studio Code add-ons.](https://godotengine.org/article/csharp-vs-and-vscode)
+
+#### Navigation
+
+- [New NavigationServer.](https://godotengine.org/article/navigation-server-godot-4-0)
+ - Support for dynamic obstacle avoidance.
+
+#### Networking
+
+- [Support for DTLS encryption in UDP and ENet.](https://godotengine.org/article/enet-dtls-encryption)
+
+#### Porting
+
+- New DisplayServer abstraction, allowing for the creation of multiple windows.
+ - This is used in the editor for detachable docks, but can also be used in projects.
+- **Android:** [Allow basic user data backup. This can be disabled in the export preset if needed.](https://github.com/godotengine/godot/pull/49069)
+- **Android:** [Support for changing the mouse cursor shape (no custom images)](https://github.com/godotengine/godot/pull/44201).
+- **iOS:** [The targeted device family (iPhone, iPad, iPhone and iPad) can now be specified in the export preset.](https://github.com/godotengine/godot/pull/49137)
+
+#### Physics
+
+- New CharacterBody node to supersede KinematicBody.
+ - Some KinematicBody features were moved to PhysicsBody.
+
+#### Porting
+
+- **Android:** Clients of the Godot library can now add their own command line arguments.
+
+#### Rendering
+
+- New Vulkan renderer.
+- [New OpenGL renderer, using OpenGL 3.3/OpenGL ES 3.0/WebGL 2.0 as a baseline.](https://github.com/godotengine/godot/pull/53150)
+ - Designed to target mobile/web platforms first, but also usable on desktop platforms.
+ - Uses a low-end-friendly approach to maximize performance in simple scenes.
+ - Currently supports 2D rendering only.
+ - OpenGL 3D rendering is planned for a future 4.x release.
+- Support for specular mapping when using 2D lighting.
+- [New DirectionalLight2D node for 2D lighting.](https://github.com/godotengine/godot/pull/43297)
+- CanvasGroup node to modulate several 2D nodes as a group (or apply shaders to them).
+- Support for clipping in CanvasItem, replacing the use of Light2D as masks in a more convenient manner.
+- [Support for light projectors/"cookies" in OmniLight3D and SpotLight3D.](https://github.com/godotengine/godot/pull/37887)
+ - Only supported for lights with shadows enabled.
+- 3D lights now have a Size property which can be set to simulate area lights.
+ - This property also affects how fast shadow penumbras will grow over distance.
+ - A shadow blur property is also available to set a constant blurring factor on a per-light basis.
+- Shadow mapping with improved filtering and PCSS-like penumbra simulation.
+ - Shadow normal offset bias is now implemented to avoid issues with shadow acne or peter-panning.
+- [New Decal node to project textures onto 3D surfaces.](https://github.com/godotengine/godot/pull/37861)
+- New fully real-time VoxelGI (formerly GIProbe).
+ - Dynamic lights and emissive can emit GI that's updated every frame (instead of only updating sporadically).
+ - Dynamic objects can receive GI and contribute to it.
+- [New signed distance field-based global illumination](https://godotengine.org/article/godot-40-gets-sdf-based-real-time-global-illumination) (SDFGI) for open world lighting.
+ - Enabled in the WorldEnvironment. No node required, no baking.
+ - Semi-realtime: dynamic objects can receive GI, but not contribute to it.
+- [Volumetric fog](https://github.com/godotengine/godot/pull/41213) with optional GI contribution.
+- [Fog volumes to locally apply volumetric fog (or subtract to global fog using negative density).](https://github.com/godotengine/godot/pull/53353)
+- More physically accurate exponential fog to replace the old distance-based fog.
+- New Aerial Scattering property in distance-based fog to fade out to the background sky instead of a fixed color.
+ - Also available in volumetric fog with the Ambient Inject property.
+- [New GPU-based lightmapper.](https://godotengine.org/article/godot-40-will-get-new-modernized-lightmapper)
+ - When using a dedicated GPU, this results in much faster bake speeds compared to the CPU lightmapper.
+ - Optional support for storing directional lighting information and rough reflections using spherical harmonics.
+ - Improved support for lighting dynamic objects with better performance and quality.
+ - In addition to automatic generation, LightmapProbe nodes can now be placed manually to provide better lighting information for dynamic objects where needed.
+- [Physical sky material and custom sky shaders](https://godotengine.org/article/custom-sky-shaders-godot-4-0), both supporting real-time updates.
+- [Global and per-instance shader uniforms.](https://godotengine.org/article/godot-40-gets-global-and-instance-shader-uniforms)
+ - This can be used to better reuse shaders, leading to improved performance.
+- Support for automatically generating and using mesh LODs to improve performance.
+ - Several LOD levels are generated for imported 3D scenes by default.
+ - LODs are automatically used for mesh rendering using a pixel coverage-based selection algorithm.
+ - Uses the [meshoptimizer](https://github.com/zeux/meshoptimizer) library.
+- Support for LOD visibility ranges in GeometryInstance3D.
+ - Manually authored LODs can be configured using distance and hysteresis cutoffs.
+ - Can be used for <abbr title="Hierarchical Level of Detail">HLOD</abbr> setups to reduce draw calls while preserving culling opportunities when up close.
+- [Support for GeometryInstance3D distance fade to make distant meshes disappear smoothly without having to modify their material.](https://github.com/godotengine/godot/pull/54222)
+- Support for automatically generating and using shadow meshes to improve performance.
+ - The generated shadow meshes are welded aggressively to improve performance with no difference in visual quality.
+ - To further improve performance, hand-made shadow meshes can be specified in the inspector in MeshInstance nodes.
+- [Support for rendering a viewport's 3D contents at a lower resolution to improve performance](https://github.com/godotengine/godot/pull/51870).
+ - 2D elements remain at full resolution to improve perceived sharpness.
+ - A scaling factor above 1.0 can be used for supersampling, which is useful to maximize quality for offline rendering.
+- See individual progress reports for more information:
+ [#1](https://godotengine.org/article/vulkan-progress-report-1),
+ [#2](https://godotengine.org/article/vulkan-progress-report-2),
+ [#3](https://godotengine.org/article/vulkan-progress-report-3),
+ [#4](https://godotengine.org/article/vulkan-progress-report-4),
+ [#5](https://godotengine.org/article/vulkan-progress-report-5),
+ [#6](https://godotengine.org/article/vulkan-progress-report-6),
+ [#7](https://godotengine.org/article/vulkan-progress-report-7).
+
+#### Shaders
+
+- New shader compiler rewritten from scratch.
+ - [Support for uniform arrays (including sampler arrays).](https://github.com/godotengine/godot/pull/49485)
+ - [Arrays can now be passed as function parameters (including arrays of structs).](https://github.com/godotengine/godot/pull/48933)
+ - [The return type of a function can now be an array (including arrays of structs).](https://github.com/godotengine/godot/pull/48933)
+ - [Array size can now be optionally written before the identifier (`int[2] array;` instead of `int array[2]`).](https://github.com/godotengine/godot/pull/53527)
+ - This eases porting shaders from GLSL.
+ - [Array constructors can now be called at any time after initialization.](https://github.com/godotengine/godot/pull/44705)
+ - For example, `int array[3]; array = {1, 2, 3}` is now valid.
+ - [New `fma()` (fused multiply-add) built-in function to optimize shaders in a low-level way.](https://github.com/godotengine/godot/pull/36225)
+ - [New built-in data (un)packing functions to optimize shaders in a low-level way.](https://github.com/godotengine/godot/pull/53066)
+ - Warning system for common issues such as floating-point comparison and unused variables.
+ - Argument names now appear in code completion tooltips.
+ - More information in the [progress report](https://godotengine.org/article/improvements-shaders-visual-shaders-godot-4).
+- [Add Billboard mode to visual shaders.](https://github.com/godotengine/godot/pull/49157)
+- [The constants `PI`, `TAU` and `E` are now available in the shader language.](https://github.com/godotengine/godot/pull/48837)
+
+#### Miscellaneous
+
+- The engine is now unit-tested using [doctest](https://github.com/onqtam/doctest).
+ - [GDScript also now has integration tests.](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript)
+- Switched from Travis CI and AppVeyor to GitHub Actions.
+- A Fish shell completion file is now available for the Godot editor's command line interface.
+
+### Changed
+
+#### Audio
+
+- [Increased the default AudioStreamPlayer3D unit size to (1 → 10) to make sounds more audible while setting up the node.](https://github.com/godotengine/godot/pull/38224)
+- Renamed the audio-related `FFT_Size` enum to `FFTSize` for consistency.
+
+#### Core
+
+- Tweaked the output strings to be more human-readable when printing various built-in Variant and Object types.
+- Renamed File's `endian_swap` property to `big_endian` for consistency with ResourceSaver and StreamPeer.
+- [Renamed File's `get_len()` method to `get_length()`.](https://github.com/godotengine/godot/pull/49061)
+- [Renamed Object's `PROPERTY_USAGE_NOEDITOR` to `PROPERTY_USAGE_NO_EDITOR`.](https://github.com/godotengine/godot/pull/54571)
+- Renamed `Vector2.clamped()` to `Vector2.limit_length()` to differentiate it from the new `Vector2.clamp()`.
+- Renamed `rand_range()` to `randf_range()` to avoid ambiguity with the new `randi_range()` and make its return type more obvious.
+- Replaced `Node.add_child_below_node()` with `Node.add_sibling()`.
+- Replaced `Directory.list_dir_begin()`'s `skip_navigational` and `skip_hidden` arguments with `show_navigational` and `show_hidden`.
+ - Both arguments are `false` by default, which means the default behavior is now to exclude both navigational and hidden files from the returned list.
+- Renamed the built-in Quat type to Quaternion.
+- Renamed the built-in Transform type to Transform3D.
+- Renamed Node3D's `translation` property to `position` for consistency with Node2D.
+- Moved YSort functionality to a Node2D property.
+- Viewports now use a size of 512×512 by default to make them visible out of the box.
+- [Screen orientation is now represented as an enum in the Project Settings.](https://github.com/godotengine/godot/pull/48939)
+- [Renamed 3D nodes to contain an explicit "3D" prefix for clarity and consistency](https://github.com/godotengine/godot/pull/37340).
+- Renamed various nodes:
+ - Spatial → Node3D
+ - GIProbe → VoxelGI
+ - BakedLightmap → LightmapGI
+ - Light2D -> PointLight2D
+ - VisibilityNotifier2D -> VisibleOnScreenNotifier2D
+ - VisibilityNotifier3D -> VisibleOnScreenNotifier3D
+ - VisibilityEnabler2D -> VisibleOnScreenEnabler2D
+ - VisibilityEnabler3D -> VisibleOnScreenEnabler3D
+- Renamed various resources:
+ - GradientTexture -> GradientTexture1D
+- Old node and resource names are automatically converted when loading scenes from Godot 3.x.
+
+#### Editor
+
+- Renewed the editor theme for a more modern design.
+ - [Increased icon saturation by 30% when using a dark theme.](https://github.com/godotengine/godot/pull/48644)
+ - Icon saturation can now be adjusted in the Editor Settings.
+- [Improved the audio bus editor appearance.](https://github.com/godotengine/godot/pull/49130)
+- Improved layout and texts of the Manage Editor Features dialog.
+- Improved the Video RAM debugger usability.
+ - The Video RAM tab is now refreshed automatically when switching to it.
+- Hovering layer checkboxes in the inspector now results in visual feedback.
+ - Clicking between two checkboxes will now enable the checkbox that was last highlighted instead of doing nothing.
+- CSV profiler measures can now be saved anywhere on the filesystem, not just in the project folder.
+- Improved the 2D zooming algorithm to always visit powers of two (50%, 100%, 200%, …) and avoid floating-point precision issues.
+- Times are now displayed as milliseconds in the profiler and performance monitors (instead of seconds).
+- Improved the batch rename dialog usability and design consistency.
+ - Clarified error messages when there are regular expression errors.
+- Optimized editor icon generation to speed up editor startup.
+- Script editor autocompletion now displays previews next to color constant suggestions.
+- The number of replaced results now appears in place of the matches counter when replacing text in the script editor.
+- Pressing <kbd>Enter</kbd> (or <kbd>Shift + Enter</kbd>) in the script editor replacement dialog now performs a forwards (or backwards) replacement operation.
+- Pressing <kbd>Ctrl + F</kbd> now focuses the search field in the AssetLib tab.
+- Pressing <kbd>G</kbd> now switches to the Pan mode in the 2D editor.
+ - The TileMap editor's Bucket Fill shortcut was moved to <kbd>B</kbd> to cater for this change.
+- Mouse wheel behavior for zooming in the animation behavior is now inverted.
+- The Sync Scene Changes and Sync Script Changes settings' values now persist on a per-project basis instead of being always enabled by default.
+- Various tooltips have been added or modified to clarify the editor operation.
+- Various visual and formatting changes to the editor help to improve readability and be closer to the online class reference.
+- Tweaked Camera2D editor line colors for better visibility.
+- Light theme presets now use a negative contrast rate by default for a more logical preview of UI elevation.
+- Increased the use of bold fonts throughout the editor.
+- Revised icons for the Gradient and GradientTexture resources.
+- Renamed "Identifier" to "Bundle Identifier" in the macOS and iOS export presets for clarity.
+- Renamed the script editor's "Adaptive" syntax theme to "Default" and "Default" to "Godot 2", for consistency with the editor theme presets.
+- Flipped the 2D editor icon to match Godot's coordinate handedness.
+
+#### GUI
+
+- Improved drive letter handling in EditorFileDialog and FileDialog.
+- Container nodes (except PanelContainer) now use the Pass mouse mode by default.
+- Pressing the left/right arrows while having selected text will now move the cursor to the beginning/end of the selection in LineEdit (while unselecting the text as usual).
+- TextEdit's `search()` method now returns a Dictionary instead of a PackedIntArray.
+- **macOS:** The <kbd>Ctrl + A</kbd> and <kbd>Ctrl + E</kbd> navigation shortcuts now work in LineEdit.
+
+#### Input
+
+- Renamed InputEventKey's `scancode` to `keycode`.
+- Renamed InputMap's `get_action_list()` to `get_action_events()`.
+
+#### Networking
+
+- Optimized bandwidth usage in the high-level multiplayer API.
+
+#### Physics
+
+- Split KinematicBody into the new CharacterBody node and PhysicsBody.
+- RayCast nodes are now enabled by default.
+ - The `disabled` property was renamed to `enabled` with its behavior inverted.
+- Renamed PlaneShape to WorldBoundaryShape.
+
+#### Rendering
+
+- Some Environment settings such as depth of field have been moved to a CameraEffects resource which is assigned to individual Camera nodes.
+- [The ACES Fitted tonemapping algoirthm is now used in place of the old ACES algorithm.](https://github.com/godotengine/godot/pull/52476)
+ - The old non-fitted ACES tonemapping algorithm was removed.
+- Quality settings have been moved from individual nodes and resources to the Project Settings for better centralization.
+- Quality settings now have performance hints in their values' names, such as "Fast" or "Slow".
+
+#### Shaders
+
+- Renamed the `.shader` file extension to `.gdshader`.
+ - Existing text-based shader files will have to be renamed before loading the project in a new engine version.
+
+#### Miscellaneous
+
+- Renamed the `x11` platform to `linuxbsd` to prepare for Wayland support.
+- The engine is now written in C++17.
+- Python 3.6 and SCons 3.1 are now required to build Godot from source.
+
+### Removed
+
+#### Buildsystem
+
+- Removed the `server` platform in favor of disabling specific DisplayServers at build-time (e.g. `vulkan=no`).
+
+#### Core
+
+- Removed the YSort node in favor of the Node2D YSort property.
+- Removed the deprecated `Color.gray()` method.
+ - Use `Color.v()` for a better grayscale approximation instead.
+- Removed built-in HQ2X implementation (used for crude hiDPI support in the default project theme).
+ - This helps with binary size as HQ2X is made of particularly large functions.
+
+#### Editor
+
+- Removed the **Dim Dialog on Editor Popup** editor setting since it was made obsolete by the multi-window paradigm.
+
+#### GUI
+
+- [Removed the ToolButton node in favor of Button.](https://github.com/godotengine/godot/pull/39690)
+ - Existing ToolButton nodes from Godot 3.x projects will be converted to Button nodes.
+
+#### Input
+
+- Removed the `DisplayServer.get_latin_keyboard_variant()` method (replaced by the more flexible `DisplayServer.keyboard_get_current_layout()`).
+
+#### Networking
+
+- Removed the deprecated `allow_object_decoding` property from PacketPeer.
+- Removed the deprecated `sync` and `slave` high-level multiplayer keywords.
+
+#### Export
+
+- **iOS:** [Remove redundant orientation export setting in favor of the orientation project setting.](https://github.com/godotengine/godot/pull/48939)
+
+#### Physics
+
+- Removed the deprecated PhysicsBody `friction` and `bounce` properties (replaced by PhysicsMaterial).
+
+#### Rendering
+
+- Removed OpenGL ES 2.0 renderer (replaced by the new mobile-oriented OpenGL 3 renderer).
+ - Vulkan, OpenGL 3.3, OpenGL ES 3.0 or WebGL 2.0 support is now required to run Godot.
+- [Removed support for 16× MSAA due to driver bugs and low performance.](https://github.com/godotengine/godot/pull/49063)
+ - For high-quality offline rendering, using supersampling together with 8× MSAA is a better option anyway.
+
+### Fixed
+
+#### Core
+
+- The positional command line argument now considers `.res` and `.tres` files as runnable scene formats.
+ - This fixes Godot not running the main scene or a custom scene if they were saved with a `.res` or `.tres` extension.
+- **macOS/Linux:** Fix the result of `Directory.get_space_left()`.
+- **Windows:** Godot can now kill its own PID using `OS.kill()`.
+
+#### Editor
+
+- The Android exporter no longer reports progress on each file, greatly speeding up the exporting process.
+- Searching with the Whole Words option enabled in the script editor is no longer exceedingly slow.
+
+#### GUI
+
+- Fixed OptionButton minimum size.
+- TabContainer is no longer too large when tabs are hidden.
+- ScrollBar now allows using `scroll_to_line()` when Scroll Active is disabled.
+- DynamicFont outlines now have antialiasing disabled if it was disabled on the font itself.
+
+#### Porting
+
+- **Windows:** `OS.execute()` now only quotes command line arguments if they contain special characters.
+
## [3.2] - 2020-01-29
### Added
diff --git a/DONORS.md b/DONORS.md
index 33a68650d9..f7026d9470 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -13,8 +13,6 @@ generous deed immortalized in the next stable release of Godot Engine.
## Platinum sponsors
Gamblify <https://www.gamblify.com>
- Heroic Labs <https://heroiclabs.com>
- Spiffcode <http://www.spiffcode.com>
## Gold sponsors
diff --git a/SConstruct b/SConstruct
index 83bd1560ff..1922e904d0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -149,7 +149,7 @@ opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable",
opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
opts.Add("disable_classes", "Disable given classes (comma separated)", "")
opts.Add(BoolVariable("modules_enabled_by_default", "If no, disable all modules except ones explicitly enabled", True))
-opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", False))
+opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", True))
opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
@@ -329,6 +329,9 @@ if env_base["target"] == "debug":
if env_base["use_precise_math_checks"]:
env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
+if not env_base.File("#main/splash_editor.png").exists():
+ # Force disabling editor splash if missing.
+ env_base["no_editor_splash"] = True
if env_base["no_editor_splash"]:
env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
diff --git a/core/doc_data.h b/core/doc_data.h
index c75cdfcde5..066cc6b848 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -70,18 +70,29 @@ public:
Vector<int> errors_returned;
bool operator<(const MethodDoc &p_method) const {
if (name == p_method.name) {
- // Must be a constructor since there is no overloading.
- // We want this arbitrary order for a class "Foo":
- // - 1. Default constructor: Foo()
- // - 2. Copy constructor: Foo(Foo)
- // - 3+. Other constructors Foo(Bar, ...) based on first argument's name
- if (arguments.size() == 0 || p_method.arguments.size() == 0) { // 1.
+ // Must be an operator or a constructor since there is no other overloading
+ if (name.left(8) == "operator") {
+ if (arguments.size() == p_method.arguments.size()) {
+ if (arguments.size() == 0) {
+ return false;
+ }
+ return arguments[0].type < p_method.arguments[0].type;
+ }
return arguments.size() < p_method.arguments.size();
+ } else {
+ // Must be a constructor
+ // We want this arbitrary order for a class "Foo":
+ // - 1. Default constructor: Foo()
+ // - 2. Copy constructor: Foo(Foo)
+ // - 3+. Other constructors Foo(Bar, ...) based on first argument's name
+ if (arguments.size() == 0 || p_method.arguments.size() == 0) { // 1.
+ return arguments.size() < p_method.arguments.size();
+ }
+ if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2.
+ return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type);
+ }
+ return arguments[0] < p_method.arguments[0];
}
- if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2.
- return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type);
- }
- return arguments[0] < p_method.arguments[0];
}
return name < p_method.name;
}
diff --git a/doc/classes/SpriteBase3D.xml b/doc/classes/SpriteBase3D.xml
index b723c9f4d4..32abd1caea 100644
--- a/doc/classes/SpriteBase3D.xml
+++ b/doc/classes/SpriteBase3D.xml
@@ -60,7 +60,8 @@
If [code]true[/code], texture is flipped vertically.
</member>
<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color(1, 1, 1, 1)">
- A color value that gets multiplied on, could be used for mood-coloring or to simulate the color of light.
+ A color value used to [i]multiply[/i] the texture's colors. Can be used for mood-coloring or to simulate the color of light.
+ [b]Note:[/b] If a [member GeometryInstance3D.material_override] is defined on the [SpriteBase3D], the material override must be configured to take vertex colors into account for albedo. Otherwise, the color defined in [member modulate] will be ignored. For a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] must be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/color] must be inserted in the shader's [code]fragment()[/code] function.
</member>
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
The texture's drawing offset.
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 1f32bba5a1..ad1e5c0050 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -4,8 +4,9 @@
Tabbed container.
</brief_description>
<description>
- Sets the active tab's [code]visible[/code] property to the value [code]true[/code]. Sets all other children's to [code]false[/code].
+ Arranges [Control] children into a tabbed view, creating a tab for each one. The active tab's corresponding [Control] has its [code]visible[/code] property set to [code]true[/code], and all other children's to [code]false[/code].
Ignores non-[Control] children.
+ [b]Note:[/b] The drawing of the clickable tabs themselves is handled by this node. Adding [TabBar]s as children is not needed.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml
index be41cdde99..97cbf6918d 100644
--- a/doc/classes/Transform2D.xml
+++ b/doc/classes/Transform2D.xml
@@ -225,17 +225,17 @@
</description>
</operator>
<operator name="operator *">
- <return type="Transform2D" />
- <argument index="0" name="right" type="Transform2D" />
+ <return type="Rect2" />
+ <argument index="0" name="right" type="Rect2" />
<description>
- Composes these two transformation matrices by multiplying them together. This has the effect of transforming the second transform (the child) by the first transform (the parent).
+ Transforms (multiplies) the [Rect2] by the given [Transform2D] matrix.
</description>
</operator>
<operator name="operator *">
- <return type="Rect2" />
- <argument index="0" name="right" type="Rect2" />
+ <return type="Transform2D" />
+ <argument index="0" name="right" type="Transform2D" />
<description>
- Transforms (multiplies) the [Rect2] by the given [Transform2D] matrix.
+ Composes these two transformation matrices by multiplying them together. This has the effect of transforming the second transform (the child) by the first transform (the parent).
</description>
</operator>
<operator name="operator *">
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index 511574f6aa..e62cb9216e 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -152,6 +152,13 @@
</description>
</operator>
<operator name="operator *">
+ <return type="AABB" />
+ <argument index="0" name="right" type="AABB" />
+ <description>
+ Transforms (multiplies) the [AABB] by the given [Transform3D] matrix.
+ </description>
+ </operator>
+ <operator name="operator *">
<return type="PackedVector3Array" />
<argument index="0" name="right" type="PackedVector3Array" />
<description>
@@ -166,13 +173,6 @@
</description>
</operator>
<operator name="operator *">
- <return type="AABB" />
- <argument index="0" name="right" type="AABB" />
- <description>
- Transforms (multiplies) the [AABB] by the given [Transform3D] matrix.
- </description>
- </operator>
- <operator name="operator *">
<return type="Vector3" />
<argument index="0" name="right" type="Vector3" />
<description>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index 595af6222c..431815e75a 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -358,19 +358,19 @@
</operator>
<operator name="operator *">
<return type="Vector2" />
- <argument index="0" name="right" type="Vector2" />
+ <argument index="0" name="right" type="Transform2D" />
<description>
- Multiplies each component of the [Vector2] by the components of the given [Vector2].
- [codeblock]
- print(Vector2(10, 20) * Vector2(3, 4)) # Prints "(30, 80)"
- [/codeblock]
+ Inversely transforms (multiplies) the [Vector2] by the given [Transform2D] transformation matrix.
</description>
</operator>
<operator name="operator *">
<return type="Vector2" />
- <argument index="0" name="right" type="Transform2D" />
+ <argument index="0" name="right" type="Vector2" />
<description>
- Inversely transforms (multiplies) the [Vector2] by the given [Transform2D] transformation matrix.
+ Multiplies each component of the [Vector2] by the components of the given [Vector2].
+ [codeblock]
+ print(Vector2(10, 20) * Vector2(3, 4)) # Prints "(30, 80)"
+ [/codeblock]
</description>
</operator>
<operator name="operator *">
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 62d467c505..6d20cc85ea 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -373,16 +373,6 @@
</operator>
<operator name="operator *">
<return type="Vector3" />
- <argument index="0" name="right" type="Vector3" />
- <description>
- Multiplies each component of the [Vector3] by the components of the given [Vector3].
- [codeblock]
- print(Vector3(10, 20, 30) * Vector3(3, 4, 5)) # Prints "(30, 80, 150)"
- [/codeblock]
- </description>
- </operator>
- <operator name="operator *">
- <return type="Vector3" />
<argument index="0" name="right" type="Basis" />
<description>
Inversely transforms (multiplies) the [Vector3] by the given [Basis] matrix.
@@ -404,6 +394,16 @@
</operator>
<operator name="operator *">
<return type="Vector3" />
+ <argument index="0" name="right" type="Vector3" />
+ <description>
+ Multiplies each component of the [Vector3] by the components of the given [Vector3].
+ [codeblock]
+ print(Vector3(10, 20, 30) * Vector3(3, 4, 5)) # Prints "(30, 80, 150)"
+ [/codeblock]
+ </description>
+ </operator>
+ <operator name="operator *">
+ <return type="Vector3" />
<argument index="0" name="right" type="float" />
<description>
Multiplies each component of the [Vector3] by the given [float].
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 0418f29808..4a3f99696d 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -25,13 +25,13 @@
<method name="find_world_2d" qualifiers="const">
<return type="World2D" />
<description>
- Returns the 2D world of the viewport.
+ Returns the first valid [World2D] for this viewport, searching the [member world_2d] property of itself and any Viewport ancestor.
</description>
</method>
<method name="find_world_3d" qualifiers="const">
<return type="World3D" />
<description>
- Returns the 3D world of the viewport, or if none the world of the parent viewport.
+ Returns the first valid [World3D] for this viewport, searching the [member world_3d] property of itself and any Viewport ancestor.
</description>
</method>
<method name="get_camera_2d" qualifiers="const">
diff --git a/doc/classes/float.xml b/doc/classes/float.xml
index c96360e6ba..9effe9d5bf 100644
--- a/doc/classes/float.xml
+++ b/doc/classes/float.xml
@@ -62,10 +62,13 @@
</description>
</operator>
<operator name="operator *">
- <return type="float" />
- <argument index="0" name="right" type="float" />
+ <return type="Color" />
+ <argument index="0" name="right" type="Color" />
<description>
- Multiplies two [float]s.
+ Multiplies each component of the [Color] by the given [float].
+ [codeblock]
+ print(1.5 * Color(0.5, 0.5, 0.5)) # Color(0.75, 0.75, 0.75)
+ [/codeblock]
</description>
</operator>
<operator name="operator *">
@@ -113,13 +116,10 @@
</description>
</operator>
<operator name="operator *">
- <return type="Color" />
- <argument index="0" name="right" type="Color" />
+ <return type="float" />
+ <argument index="0" name="right" type="float" />
<description>
- Multiplies each component of the [Color] by the given [float].
- [codeblock]
- print(1.5 * Color(0.5, 0.5, 0.5)) # Color(0.75, 0.75, 0.75)
- [/codeblock]
+ Multiplies two [float]s.
</description>
</operator>
<operator name="operator *">
diff --git a/doc/classes/int.xml b/doc/classes/int.xml
index bb36d83741..d212fe42bf 100644
--- a/doc/classes/int.xml
+++ b/doc/classes/int.xml
@@ -132,20 +132,6 @@
</description>
</operator>
<operator name="operator *">
- <return type="int" />
- <argument index="0" name="right" type="int" />
- <description>
- Multiplies two [int]s.
- </description>
- </operator>
- <operator name="operator *">
- <return type="float" />
- <argument index="0" name="right" type="float" />
- <description>
- Multiplies an [int] and a [float]. The result is a [float].
- </description>
- </operator>
- <operator name="operator *">
<return type="Vector2" />
<argument index="0" name="right" type="Vector2" />
<description>
@@ -176,6 +162,20 @@
Multiplies each component of the [Vector3i] by the given [int].
</description>
</operator>
+ <operator name="operator *">
+ <return type="float" />
+ <argument index="0" name="right" type="float" />
+ <description>
+ Multiplies an [int] and a [float]. The result is a [float].
+ </description>
+ </operator>
+ <operator name="operator *">
+ <return type="int" />
+ <argument index="0" name="right" type="int" />
+ <description>
+ Multiplies two [int]s.
+ </description>
+ </operator>
<operator name="operator +">
<return type="float" />
<argument index="0" name="right" type="float" />
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index f9443e11db..168080f2f1 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -758,6 +758,10 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
code += a + " - " + n + " * (" + a + " / " + n + "))";
} break;
+ case SL::OP_EMPTY: {
+ // Semicolon (or empty statement) - ignored.
+ } break;
+
default: {
if (p_use_scope) {
code += "(";
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index bfcd2dd4ca..1f01e9d4cf 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1314,10 +1314,10 @@ void CodeTextEditor::delete_lines() {
int count = Math::abs(to_line - from_line) + 1;
text_editor->set_caret_line(from_line, false);
+ text_editor->deselect();
for (int i = 0; i < count; i++) {
_delete_line(from_line);
}
- text_editor->deselect();
} else {
_delete_line(text_editor->get_caret_line());
}
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index a163b468e6..6fd8cb47ea 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -893,8 +893,13 @@ bool EditorData::script_class_is_parent(const String &p_class, const String &p_i
if (!ScriptServer::is_global_class(p_class)) {
return false;
}
- String base = script_class_get_base(p_class);
+
Ref<Script> script = script_class_load_script(p_class);
+ if (script.is_null()) {
+ return false;
+ }
+
+ String base = script_class_get_base(p_class);
Ref<Script> base_script = script->get_base_script();
while (p_inherits != base) {
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 5c25e6aae9..7458f617c3 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -497,7 +497,7 @@ void LocalizationEditor::update_translations() {
TreeItem *t = translation_filter->create_item(root);
t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- t->set_text(0, n);
+ t->set_text(0, vformat("[%s] %s", l, n));
t->set_editable(0, true);
t->set_tooltip(0, l);
t->set_checked(0, is_checked);
@@ -537,7 +537,7 @@ void LocalizationEditor::update_translations() {
if (langnames.length() > 0) {
langnames += ",";
}
- langnames += names[i];
+ langnames += vformat("[%s] %s", langs[i], names[i]);
translation_locales_idxs_remap.write[l_idx] = i;
l_idx++;
}
@@ -546,7 +546,7 @@ void LocalizationEditor::update_translations() {
if (i > 0) {
langnames += ",";
}
- langnames += names[i];
+ langnames += vformat("[%s] %s", langs[i], names[i]);
}
}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 75d2bed1b2..c4a938f91d 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -945,7 +945,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
add_node->set_text(TTR("Add Node..."));
graph->get_zoom_hbox()->move_child(add_node, 0);
add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node));
- add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu));
+ add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu), varray(false));
add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot", 2));
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index a3378d1550..d9ce7cfd94 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -2339,7 +2339,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (selection2.size() > 0) {
drag_type = DRAG_MOVE;
- drag_from = click;
+ drag_from = drag_start_origin;
_save_canvas_item_state(drag_selection);
}
return true;
@@ -5267,7 +5267,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
select_button->set_pressed(true);
select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), Key::Q));
select_button->set_shortcut_context(this);
- select_button->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked."));
+ select_button->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked."));
hb->add_child(memnew(VSeparator));
@@ -5296,7 +5296,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_SCALE));
scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), Key::S));
scale_button->set_shortcut_context(this);
- scale_button->set_tooltip(TTR("Scale Mode"));
+ scale_button->set_tooltip(TTR("Shift: Scale proportionally."));
hb->add_child(memnew(VSeparator));
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 44c789b145..4b50f484a4 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -57,6 +57,27 @@ void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
emission_mask->popup_centered();
}
+void GPUParticles2DEditorPlugin::_selection_changed() {
+ List<Node *> selected_nodes = editor->get_editor_selection()->get_selected_node_list();
+
+ if (selected_particles.is_empty() && selected_nodes.is_empty()) {
+ return;
+ }
+
+ for (GPUParticles2D *SP : selected_particles) {
+ SP->set_show_visibility_rect(false);
+ }
+ selected_particles.clear();
+
+ for (Node *P : selected_nodes) {
+ GPUParticles2D *selected_particle = Object::cast_to<GPUParticles2D>(P);
+ if (selected_particle != nullptr) {
+ selected_particle->set_show_visibility_rect(true);
+ selected_particles.push_back(selected_particle);
+ }
+ }
+}
+
void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
switch (p_idx) {
case MENU_GENERATE_VISIBILITY_RECT: {
@@ -334,6 +355,7 @@ void GPUParticles2DEditorPlugin::_notification(int p_what) {
menu->get_popup()->connect("id_pressed", callable_mp(this, &GPUParticles2DEditorPlugin::_menu_callback));
menu->set_icon(menu->get_theme_icon(SNAME("GPUParticles2D"), SNAME("EditorIcons")));
file->connect("file_selected", callable_mp(this, &GPUParticles2DEditorPlugin::_file_selected));
+ EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &GPUParticles2DEditorPlugin::_selection_changed));
}
}
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index 0b2028b745..bdfc021aa7 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -56,6 +56,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
};
GPUParticles2D *particles;
+ List<GPUParticles2D *> selected_particles;
EditorFileDialog *file;
EditorNode *editor;
@@ -79,6 +80,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
void _menu_callback(int p_idx);
void _generate_visibility_rect();
void _generate_emission_mask();
+ void _selection_changed();
protected:
void _notification(int p_what);
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 74fbef3caf..154f9bd6b7 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -2579,7 +2579,7 @@ void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
create_material("particles_material", gizmo_color);
- gizmo_color.a = 0.1;
+ gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
create_material("particles_solid_material", gizmo_color);
create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoGPUParticles3D"), SNAME("EditorIcons")));
create_handle_material("handles");
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index d3b462cda5..0a8cfa3815 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -1749,7 +1749,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else {
const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
if (clicked.is_valid() && movement_threshold_passed) {
- _compute_edit(_edit.mouse_pos);
+ _compute_edit(_edit.original_mouse_pos);
clicked = ObjectID();
_edit.mode = TRANSFORM_TRANSLATE;
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 73b1fc7c67..37d52174f9 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -598,7 +598,10 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
- // Do nothing.
+ // Cancel tile pasting on right-click
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ drag_type = DRAG_TYPE_NONE;
+ }
} else if (tool_buttons_group->get_pressed_button() == select_tool_button) {
drag_start_mouse_pos = mpos;
if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 372a77f67d..9f8a42eccc 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -2743,7 +2743,7 @@ ProjectManager::ProjectManager() {
for (int i = 0; i < editor_languages.size(); i++) {
String lang = editor_languages[i];
String lang_name = TranslationServer::get_singleton()->get_locale_name(lang);
- language_btn->add_item(lang_name + " [" + lang + "]", i);
+ language_btn->add_item(vformat("[%s] %s", lang, lang_name), i);
language_btn->set_item_metadata(i, lang);
if (current_lang == lang) {
language_btn->select(i);
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 611038a947..a03871f33c 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1306,6 +1306,7 @@ void SceneTreeDock::_notification(int p_what) {
button_instance->set_icon(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
button_create_script->set_icon(get_theme_icon(SNAME("ScriptCreate"), SNAME("EditorIcons")));
button_detach_script->set_icon(get_theme_icon(SNAME("ScriptRemove"), SNAME("EditorIcons")));
+ button_tree_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
button_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons")));
button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons")));
button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons")));
diff --git a/main/SCsub b/main/SCsub
index 87d64e48f9..79dc4bff15 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -20,12 +20,13 @@ env_main.CommandNoCache(
env.Run(main_builders.make_splash, "Building splash screen header."),
)
-env_main.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
-env_main.CommandNoCache(
- "#main/splash_editor.gen.h",
- "#main/splash_editor.png",
- env.Run(main_builders.make_splash_editor, "Building editor splash screen header."),
-)
+if not env_main["no_editor_splash"]:
+ env_main.Depends("#main/splash_editor.gen.h", "#main/splash_editor.png")
+ env_main.CommandNoCache(
+ "#main/splash_editor.gen.h",
+ "#main/splash_editor.png",
+ env.Run(main_builders.make_splash_editor, "Building editor splash screen header."),
+ )
env_main.Depends("#main/app_icon.gen.h", "#main/app_icon.png")
env_main.CommandNoCache(
diff --git a/main/main.cpp b/main/main.cpp
index b4fbc1929c..e760c930f2 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -56,7 +56,6 @@
#include "main/main_timer_sync.h"
#include "main/performance.h"
#include "main/splash.gen.h"
-#include "main/splash_editor.gen.h"
#include "modules/register_module_types.h"
#include "platform/register_platform_apis.h"
#include "scene/main/scene_tree.h"
@@ -86,6 +85,9 @@
#include "editor/editor_settings.h"
#include "editor/progress_dialog.h"
#include "editor/project_manager.h"
+#ifndef NO_EDITOR_SPLASH
+#include "main/splash_editor.gen.h"
+#endif
#endif
#include "modules/modules_enabled.gen.h" // For mono.
diff --git a/main/splash_editor.png b/main/splash_editor.png
deleted file mode 100644
index 49af9fde22..0000000000
--- a/main/splash_editor.png
+++ /dev/null
Binary files differ
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index 49e9f5f97e..a9c1f0bb9a 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -125,7 +125,7 @@ AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
Ref<AudioStreamPlaybackMP3> mp3s;
- ERR_FAIL_COND_V_MSG(data == nullptr, mp3s,
+ ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
"This AudioStreamMP3 does not have an audio file assigned "
"to it. AudioStreamMP3 should not be created from the "
"inspector or with `.new()`. Instead, load an audio file.");
@@ -134,7 +134,7 @@ Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));
- int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, (const uint8_t *)data, data_len, MP3D_SEEK_TO_SAMPLE);
+ int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);
mp3s->frames_mixed = 0;
mp3s->active = false;
@@ -152,11 +152,7 @@ String AudioStreamMP3::get_stream_name() const {
}
void AudioStreamMP3::clear_data() {
- if (data) {
- memfree(data);
- data = nullptr;
- data_len = 0;
- }
+ data.clear();
}
void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
@@ -165,7 +161,7 @@ void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
mp3dec_ex_t mp3d;
int err = mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE);
- ERR_FAIL_COND(err != 0);
+ ERR_FAIL_COND_MSG(err || mp3d.info.hz == 0, "Failed to decode mp3 file. Make sure it is a valid mp3 audio file.");
channels = mp3d.info.channels;
sample_rate = mp3d.info.hz;
@@ -175,23 +171,13 @@ void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
clear_data();
- data = memalloc(src_data_len);
- memcpy(data, src_datar, src_data_len);
+ data.resize(src_data_len);
+ memcpy(data.ptrw(), src_datar, src_data_len);
data_len = src_data_len;
}
Vector<uint8_t> AudioStreamMP3::get_data() const {
- Vector<uint8_t> vdata;
-
- if (data_len && data) {
- vdata.resize(data_len);
- {
- uint8_t *w = vdata.ptrw();
- memcpy(w, data, data_len);
- }
- }
-
- return vdata;
+ return data;
}
void AudioStreamMP3::set_loop(bool p_enable) {
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 3c8bdd8c53..e3adfe683b 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -75,7 +75,7 @@ class AudioStreamMP3 : public AudioStream {
friend class AudioStreamPlaybackMP3;
- void *data = nullptr;
+ PackedByteArray data;
uint32_t data_len = 0;
float sample_rate = 1.0;
diff --git a/platform/javascript/js/libs/library_godot_input.js b/platform/javascript/js/libs/library_godot_input.js
index 3c1c05e44e..945dbba902 100644
--- a/platform/javascript/js/libs/library_godot_input.js
+++ b/platform/javascript/js/libs/library_godot_input.js
@@ -424,9 +424,9 @@ const GodotInput = {
for (let i = 0; i < touches.length; i++) {
const touch = touches[i];
const pos = GodotInput.computePosition(touch, rect);
- GodotRuntime.setHeapValue(coords + (i * 2), pos[0], 'double');
- GodotRuntime.setHeapValue(coords + (i * 2 + 8), pos[1], 'double');
- GodotRuntime.setHeapValue(ids + i, touch.identifier, 'i32');
+ GodotRuntime.setHeapValue(coords + (i * 2) * 8, pos[0], 'double');
+ GodotRuntime.setHeapValue(coords + (i * 2 + 1) * 8, pos[1], 'double');
+ GodotRuntime.setHeapValue(ids + i * 4, touch.identifier, 'i32');
}
func(type, touches.length);
if (evt.cancelable) {
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index bf5671be19..b6902686fe 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -176,7 +176,7 @@ Transform2D Camera2D::get_camera_transform() {
Rect2 screen_rect(-screen_offset + ret_camera_pos, screen_size * zoom);
- if (!limit_smoothing_enabled) {
+ if (!smoothing_enabled || !limit_smoothing_enabled) {
if (screen_rect.position.x < limit[SIDE_LEFT]) {
screen_rect.position.x = limit[SIDE_LEFT];
}
diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp
index f1f4d1b769..4384ec8141 100644
--- a/scene/2d/gpu_particles_2d.cpp
+++ b/scene/2d/gpu_particles_2d.cpp
@@ -170,6 +170,13 @@ void GPUParticles2D::set_trail_section_subdivisions(int p_subdivisions) {
update();
}
+#ifdef TOOLS_ENABLED
+void GPUParticles2D::set_show_visibility_rect(bool p_show_visibility_rect) {
+ show_visibility_rect = p_show_visibility_rect;
+ update();
+}
+#endif
+
bool GPUParticles2D::is_trail_enabled() const {
return trail_enabled;
}
@@ -452,7 +459,7 @@ void GPUParticles2D::_notification(int p_what) {
RS::get_singleton()->canvas_item_add_particles(get_canvas_item(), particles, texture_rid);
#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() && (this == get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->is_ancestor_of(this))) {
+ if (show_visibility_rect) {
draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
}
#endif
@@ -588,6 +595,9 @@ GPUParticles2D::GPUParticles2D() {
set_speed_scale(1);
set_fixed_fps(30);
set_collision_base_size(collision_base_size);
+#ifdef TOOLS_ENABLED
+ show_visibility_rect = false;
+#endif
}
GPUParticles2D::~GPUParticles2D() {
diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h
index d7eee461b4..a9e66b3051 100644
--- a/scene/2d/gpu_particles_2d.h
+++ b/scene/2d/gpu_particles_2d.h
@@ -58,7 +58,9 @@ private:
bool local_coords;
int fixed_fps;
bool fractional_delta;
-
+#ifdef TOOLS_ENABLED
+ bool show_visibility_rect;
+#endif
Ref<Material> process_material;
DrawOrder draw_order;
@@ -81,7 +83,6 @@ protected:
static void _bind_methods();
virtual void _validate_property(PropertyInfo &property) const override;
void _notification(int p_what);
-
void _update_collision_size();
public:
@@ -102,6 +103,10 @@ public:
void set_trail_sections(int p_sections);
void set_trail_section_subdivisions(int p_subdivisions);
+#ifdef TOOLS_ENABLED
+ void set_show_visibility_rect(bool p_show_visibility_rect);
+#endif
+
bool is_emitting() const;
int get_amount() const;
double get_lifetime() const;
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index ed30e871d7..40211f2a9b 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -100,16 +100,18 @@ void Path2D::_notification(int p_what) {
#endif
const Color color = Color(0.5, 0.6, 1.0, 0.7);
- for (int i = 0; i < curve->get_point_count(); i++) {
- Vector2 prev_p = curve->get_point_position(i);
+ _cached_draw_pts.resize(curve->get_point_count() * 8);
+ int count = 0;
- for (int j = 1; j <= 8; j++) {
- real_t frac = j / 8.0;
+ for (int i = 0; i < curve->get_point_count(); i++) {
+ for (int j = 0; j < 8; j++) {
+ real_t frac = j * (1.0 / 8.0);
Vector2 p = curve->interpolate(i, frac);
- draw_line(prev_p, p, color, line_width);
- prev_p = p;
+ _cached_draw_pts.set(count++, p);
}
}
+
+ draw_polyline(_cached_draw_pts, color, line_width, true);
}
}
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 3b12f025fc..7e8478283f 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -38,6 +38,7 @@ class Path2D : public Node2D {
GDCLASS(Path2D, Node2D);
Ref<Curve2D> curve;
+ Vector<Vector2> _cached_draw_pts;
void _curve_changed();
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index ea04918988..1b07c086c0 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -198,7 +198,7 @@ private:
struct PlaybackData {
AnimationData *from = nullptr;
- float pos = 0.0;
+ double pos = 0.0;
float speed_scale = 1.0;
};
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index 3c8949ddfb..56c04b32e3 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -367,7 +367,7 @@ void SceneDebuggerObject::serialize(Array &r_arr, int p_max_size) {
PropertyHint hint = pi.hint;
String hint_string = pi.hint_string;
- if (!res.is_null()) {
+ if (!res.is_null() && !res->get_path().is_empty()) {
var = res->get_path();
} else { //only send information that can be sent..
int len = 0; //test how big is this to encode
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 57ee7fd494..c7d5a600a1 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -164,7 +164,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
- // pressed
+ // Right mouse button clicked.
emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
}
@@ -174,7 +174,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
- // pressed
+ // Close button pressed.
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -183,7 +183,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
- // clicks
+ // Clicks.
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -235,7 +235,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (tabs[i].cb_rect.has_point(pos)) {
+ if (tabs[i].cb_rect.has_point(pos) && (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current))) {
cb_pressing = true;
update();
return;
@@ -659,7 +659,7 @@ void TabBar::_update_hover() {
}
const Point2 &pos = get_local_mouse_position();
- // test hovering to display right or close button
+ // test hovering to display right or close button.
int hover_now = -1;
int hover_buttons = -1;
for (int i = offset; i < tabs.size(); i++) {
@@ -684,7 +684,7 @@ void TabBar::_update_hover() {
emit_signal(SNAME("tab_hovered"), hover);
}
- if (hover_buttons == -1) { // no hover
+ if (hover_buttons == -1) { // No hover.
rb_hover = hover_buttons;
cb_hover = hover_buttons;
}
@@ -860,7 +860,7 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
if (from_path == to_path) {
return true;
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between other TabBars
+ // Drag and drop between other TabBars.
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
@@ -895,7 +895,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
emit_signal(SNAME("active_tab_rearranged"), hover_now);
set_current_tab(hover_now);
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between Tabs
+ // Drag and drop between Tabs.
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index facbe06d4d..3c10c6bd66 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -501,6 +501,7 @@ void TextureProgressBar::_notification(int p_what) {
}
p *= get_relative_center();
+ p += progress_offset;
p = p.floor();
draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index f9e96a0784..3ac1a0eaf0 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -396,14 +396,15 @@ void Viewport::_notification(int p_what) {
#ifndef _3D_DISABLED
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
- RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, true);
+ RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, false);
RenderingServer::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, 0);
RenderingServer::get_singleton()->multimesh_set_mesh(contact_3d_debug_multimesh, get_tree()->get_debug_contact_mesh()->get_rid());
contact_3d_debug_instance = RenderingServer::get_singleton()->instance_create();
RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
- //RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS, true);
+ RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
#endif // _3D_DISABLED
+ set_physics_process_internal(true);
}
} break;
@@ -1707,12 +1708,10 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
if (mm->get_button_mask() == MouseButton::NONE) {
// Nothing pressed.
- bool can_tooltip = true;
-
bool is_tooltip_shown = false;
if (gui.tooltip_popup) {
- if (can_tooltip && gui.tooltip_control) {
+ if (gui.tooltip_control) {
String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
if (tooltip.length() == 0) {
@@ -1737,7 +1736,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
}
- if (can_tooltip && !is_tooltip_shown) {
+ if (!is_tooltip_shown && over->can_process()) {
if (gui.tooltip_timer.is_valid()) {
gui.tooltip_timer->release_connections();
gui.tooltip_timer = Ref<SceneTreeTimer>();
diff --git a/scene/resources/visual_shader_particle_nodes.cpp b/scene/resources/visual_shader_particle_nodes.cpp
index 1a829968e3..a7c20b2cb9 100644
--- a/scene/resources/visual_shader_particle_nodes.cpp
+++ b/scene/resources/visual_shader_particle_nodes.cpp
@@ -318,26 +318,24 @@ String VisualShaderNodeParticleMeshEmitter::get_input_port_name(int p_port) cons
String VisualShaderNodeParticleMeshEmitter::generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String code;
- if (mesh.is_valid()) {
- if (is_output_port_connected(0)) { // position
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
- }
+ if (is_output_port_connected(0)) { // position
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_vx") + ";\n";
+ }
- if (is_output_port_connected(1)) { // normal
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
- }
+ if (is_output_port_connected(1)) { // normal
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_nm") + ";\n";
+ }
- if (is_output_port_connected(2) || is_output_port_connected(3)) { // color & alpha
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_col") + ";\n";
- }
+ if (is_output_port_connected(2) || is_output_port_connected(3)) { // color & alpha
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_col") + ";\n";
+ }
- if (is_output_port_connected(4)) { // uv
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv") + ";\n";
- }
+ if (is_output_port_connected(4)) { // uv
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv") + ";\n";
+ }
- if (is_output_port_connected(5)) { // uv2
- code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv2") + ";\n";
- }
+ if (is_output_port_connected(5)) { // uv2
+ code += "uniform sampler2D " + make_unique_id(p_type, p_id, "mesh_uv2") + ";\n";
}
return code;
@@ -503,67 +501,97 @@ void VisualShaderNodeParticleMeshEmitter::_update_textures() {
Vector<Vector2> uvs;
Vector<Vector2> uvs2;
+ const int surface_count = mesh->get_surface_count();
+
if (use_all_surfaces) {
- for (int i = 0; i < max_surface_index; i++) {
+ for (int i = 0; i < surface_count; i++) {
+ const Array surface_arrays = mesh->surface_get_arrays(i);
+ const int surface_arrays_size = surface_arrays.size();
+
// position
- Array vertex_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_VERTEX];
- for (int j = 0; j < vertex_array.size(); j++) {
- vertices.push_back((Vector3)vertex_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_VERTEX) {
+ Array vertex_array = surface_arrays[Mesh::ARRAY_VERTEX];
+ for (int j = 0; j < vertex_array.size(); j++) {
+ vertices.push_back((Vector3)vertex_array[j]);
+ }
}
// normal
- Array normal_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_NORMAL];
- for (int j = 0; j < normal_array.size(); j++) {
- normals.push_back((Vector3)normal_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_NORMAL) {
+ Array normal_array = surface_arrays[Mesh::ARRAY_NORMAL];
+ for (int j = 0; j < normal_array.size(); j++) {
+ normals.push_back((Vector3)normal_array[j]);
+ }
}
// color
- Array color_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_COLOR];
- for (int j = 0; j < color_array.size(); j++) {
- colors.push_back((Color)color_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_COLOR) {
+ Array color_array = surface_arrays[Mesh::ARRAY_COLOR];
+ for (int j = 0; j < color_array.size(); j++) {
+ colors.push_back((Color)color_array[j]);
+ }
}
// uv
- Array uv_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_TEX_UV];
- for (int j = 0; j < uv_array.size(); j++) {
- uvs.push_back((Vector2)uv_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV) {
+ Array uv_array = surface_arrays[Mesh::ARRAY_TEX_UV];
+ for (int j = 0; j < uv_array.size(); j++) {
+ uvs.push_back((Vector2)uv_array[j]);
+ }
}
// uv2
- Array uv2_array = mesh->surface_get_arrays(i)[Mesh::ARRAY_TEX_UV2];
- for (int j = 0; j < uv2_array.size(); j++) {
- uvs2.push_back((Vector2)uv2_array[j]);
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV2) {
+ Array uv2_array = surface_arrays[Mesh::ARRAY_TEX_UV2];
+ for (int j = 0; j < uv2_array.size(); j++) {
+ uvs2.push_back((Vector2)uv2_array[j]);
+ }
}
}
} else {
- // position
- Array vertex_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_VERTEX];
- for (int i = 0; i < vertex_array.size(); i++) {
- vertices.push_back((Vector3)vertex_array[i]);
- }
+ if (surface_index >= 0 && surface_index < surface_count) {
+ const Array surface_arrays = mesh->surface_get_arrays(surface_index);
+ const int surface_arrays_size = surface_arrays.size();
- // normal
- Array normal_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_NORMAL];
- for (int i = 0; i < normal_array.size(); i++) {
- normals.push_back((Vector3)normal_array[i]);
- }
+ // position
+ if (surface_arrays_size > Mesh::ARRAY_VERTEX) {
+ Array vertex_array = surface_arrays[Mesh::ARRAY_VERTEX];
+ for (int i = 0; i < vertex_array.size(); i++) {
+ vertices.push_back((Vector3)vertex_array[i]);
+ }
+ }
- // color
- Array color_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_COLOR];
- for (int i = 0; i < color_array.size(); i++) {
- colors.push_back((Color)color_array[i]);
- }
+ // normal
+ if (surface_arrays_size > Mesh::ARRAY_NORMAL) {
+ Array normal_array = surface_arrays[Mesh::ARRAY_NORMAL];
+ for (int i = 0; i < normal_array.size(); i++) {
+ normals.push_back((Vector3)normal_array[i]);
+ }
+ }
- // uv
- Array uv_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_TEX_UV];
- for (int j = 0; j < uv_array.size(); j++) {
- uvs.push_back((Vector2)uv_array[j]);
- }
+ // color
+ if (surface_arrays_size > Mesh::ARRAY_COLOR) {
+ Array color_array = surface_arrays[Mesh::ARRAY_COLOR];
+ for (int i = 0; i < color_array.size(); i++) {
+ colors.push_back((Color)color_array[i]);
+ }
+ }
+
+ // uv
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV) {
+ Array uv_array = surface_arrays[Mesh::ARRAY_TEX_UV];
+ for (int j = 0; j < uv_array.size(); j++) {
+ uvs.push_back((Vector2)uv_array[j]);
+ }
+ }
- // uv2
- Array uv2_array = mesh->surface_get_arrays(surface_index)[Mesh::ARRAY_TEX_UV2];
- for (int j = 0; j < uv2_array.size(); j++) {
- uvs2.push_back((Vector2)uv2_array[j]);
+ // uv2
+ if (surface_arrays_size > Mesh::ARRAY_TEX_UV2) {
+ Array uv2_array = surface_arrays[Mesh::ARRAY_TEX_UV2];
+ for (int j = 0; j < uv2_array.size(); j++) {
+ uvs2.push_back((Vector2)uv2_array[j]);
+ }
+ }
}
}
@@ -579,12 +607,6 @@ void VisualShaderNodeParticleMeshEmitter::set_mesh(Ref<Mesh> p_mesh) {
return;
}
- if (p_mesh.is_valid()) {
- max_surface_index = p_mesh->get_surface_count();
- } else {
- max_surface_index = 0;
- }
-
if (mesh.is_valid()) {
Callable callable = callable_mp(this, &VisualShaderNodeParticleMeshEmitter::_update_textures);
@@ -623,7 +645,16 @@ bool VisualShaderNodeParticleMeshEmitter::is_use_all_surfaces() const {
}
void VisualShaderNodeParticleMeshEmitter::set_surface_index(int p_surface_index) {
- if (p_surface_index == surface_index || p_surface_index < 0 || p_surface_index >= max_surface_index) {
+ if (mesh.is_valid()) {
+ if (mesh->get_surface_count() > 0) {
+ p_surface_index = CLAMP(p_surface_index, 0, mesh->get_surface_count() - 1);
+ } else {
+ p_surface_index = 0;
+ }
+ } else if (p_surface_index < 0) {
+ p_surface_index = 0;
+ }
+ if (surface_index == p_surface_index) {
return;
}
surface_index = p_surface_index;
diff --git a/scene/resources/visual_shader_particle_nodes.h b/scene/resources/visual_shader_particle_nodes.h
index 79459432f1..ae7ea69225 100644
--- a/scene/resources/visual_shader_particle_nodes.h
+++ b/scene/resources/visual_shader_particle_nodes.h
@@ -111,7 +111,6 @@ class VisualShaderNodeParticleMeshEmitter : public VisualShaderNodeParticleEmitt
Ref<Mesh> mesh;
bool use_all_surfaces = true;
int surface_index = 0;
- int max_surface_index = 0;
Ref<ImageTexture> position_texture;
Ref<ImageTexture> normal_texture;
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index fd26efa1fb..29e4a63cbb 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -10033,7 +10033,7 @@ RendererStorageRD::RendererStorageRD() {
actions.renames["RESTART"] = "restart";
actions.renames["CUSTOM"] = "PARTICLE.custom";
actions.renames["TRANSFORM"] = "PARTICLE.xform";
- actions.renames["TIME"] = "FRAME.time";
+ actions.renames["TIME"] = "frame_history.data[0].time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
index 77d3a2e766..b02b3d2723 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
@@ -1318,6 +1318,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
code += ")";
} break;
+ case SL::OP_EMPTY: {
+ // Semicolon (or empty statement) - ignored.
+ } break;
default: {
if (p_use_scope) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 50719ecfc3..052b1d6d9f 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -4293,7 +4293,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_size(BlockNode *p_block, cons
return n;
}
-Error ShaderLanguage::_parse_global_array_size(int &r_array_size) {
+Error ShaderLanguage::_parse_global_array_size(int &r_array_size, const FunctionInfo &p_function_info) {
if (r_array_size > 0) {
_set_error("Array size is already defined!");
return ERR_PARSE_ERROR;
@@ -4305,7 +4305,7 @@ Error ShaderLanguage::_parse_global_array_size(int &r_array_size) {
if (tk.type != TK_INT_CONSTANT || ((int)tk.constant) <= 0) {
_set_tkpos(pos);
- Node *n = _parse_array_size(nullptr, FunctionInfo(), array_size);
+ Node *n = _parse_array_size(nullptr, p_function_info, array_size);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -5208,9 +5208,21 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.push_back(e);
continue;
} else {
- _set_error("Expected expression, found: " + get_token_text(tk));
- return nullptr;
- //nothing
+ if (tk.type != TK_SEMICOLON) {
+ _set_error("Expected expression, found: " + get_token_text(tk));
+ return nullptr;
+ } else {
+#if DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::FORMATTING_ERROR_FLAG)) {
+ _add_line_warning(ShaderWarning::FORMATTING_ERROR, "Empty statement. Remove ';' to fix this warning.");
+ }
+#endif // DEBUG_ENABLED
+ _set_tkpos(prepos);
+
+ OperatorNode *func = alloc_node<OperatorNode>();
+ func->op = OP_EMPTY;
+ expr = func;
+ }
}
ERR_FAIL_COND_V(!expr, nullptr);
@@ -6386,7 +6398,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
ArrayDeclarationNode::Declaration adecl;
if (tk.type != TK_IDENTIFIER && tk.type != TK_BRACKET_OPEN) {
- _set_error("Expected identifier or '[' after type.");
+ _set_error("Expected identifier or '[' after datatype.");
return ERR_PARSE_ERROR;
}
@@ -7446,6 +7458,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
stages = &p_functions;
+ const FunctionInfo &constants = p_functions.has("constants") ? p_functions["constants"] : FunctionInfo();
while (tk.type != TK_EOF) {
switch (tk.type) {
@@ -7489,7 +7502,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_IDENTIFIER) {
st.name = tk.text;
- if (shader->structs.has(st.name)) {
+ if (shader->constants.has(st.name) || shader->structs.has(st.name)) {
_set_error("Redefinition of '" + String(st.name) + "'");
return ERR_PARSE_ERROR;
}
@@ -7561,7 +7574,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
int array_size = 0;
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(array_size);
+ Error error = _parse_global_array_size(array_size, constants);
if (error != OK) {
return error;
}
@@ -7588,7 +7601,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(member->array_size);
+ Error error = _parse_global_array_size(member->array_size, constants);
if (error != OK) {
return error;
}
@@ -7715,7 +7728,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(array_size);
+ Error error = _parse_global_array_size(array_size, constants);
if (error != OK) {
return error;
}
@@ -7730,7 +7743,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
prev_pos = _get_tkpos();
name = tk.text;
- if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
+ if (_find_identifier(nullptr, false, constants, name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -7763,7 +7776,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- Error error = _parse_global_array_size(uniform2.array_size);
+ Error error = _parse_global_array_size(uniform2.array_size, constants);
if (error != OK) {
return error;
}
@@ -8017,7 +8030,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
- Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo());
+ Node *expr = _parse_and_reduce_expression(nullptr, constants);
if (!expr) {
return ERR_PARSE_ERROR;
}
@@ -8113,7 +8126,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
DataPrecision precision = PRECISION_DEFAULT;
DataType type;
StringName name;
- int return_array_size = 0;
+ int array_size = 0;
if (tk.type == TK_CONST) {
is_constant = true;
@@ -8152,13 +8165,19 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
prev_pos = _get_tkpos();
tk = _get_token();
+ bool unknown_size = false;
+
if (tk.type == TK_BRACKET_OPEN) {
+ if (is_constant && RenderingServer::get_singleton()->is_low_end()) {
+ _set_error("Global const arrays are only supported on high-end platform!");
+ return ERR_PARSE_ERROR;
+ }
bool error = false;
tk = _get_token();
if (tk.type == TK_INT_CONSTANT) {
- return_array_size = (int)tk.constant;
- if (return_array_size > 0) {
+ array_size = (int)tk.constant;
+ if (array_size > 0) {
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
_set_error("Expected ']'");
@@ -8167,11 +8186,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else {
error = true;
}
+ } else if (tk.type == TK_BRACKET_CLOSE) {
+ unknown_size = true;
} else {
error = true;
}
if (error) {
- _set_error("Expected integer constant > 0");
+ _set_error("Expected integer constant > 0 or ']'");
return ERR_PARSE_ERROR;
}
@@ -8183,16 +8204,15 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
_get_completable_identifier(nullptr, COMPLETION_MAIN_FUNCTION, name);
if (name == StringName()) {
- _set_error("Expected function name after datatype");
- return ERR_PARSE_ERROR;
- }
-
- if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
- _set_error("Redefinition of '" + String(name) + "'");
+ if (is_constant) {
+ _set_error("Expected identifier or '[' after datatype.");
+ } else {
+ _set_error("Expected function name after datatype.");
+ }
return ERR_PARSE_ERROR;
}
- if (has_builtin(p_functions, name)) {
+ if (shader->structs.has(name) || _find_identifier(nullptr, false, constants, name) || has_builtin(p_functions, name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -8205,7 +8225,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
//variable
-
+ bool first = true;
while (true) {
ShaderNode::Constant constant;
constant.name = name;
@@ -8213,16 +8233,18 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.type_str = struct_name;
constant.precision = precision;
constant.initializer = nullptr;
- constant.array_size = 0;
-
- bool unknown_size = false;
+ constant.array_size = (first ? array_size : 0);
+ first = false;
if (tk.type == TK_BRACKET_OPEN) {
if (RenderingServer::get_singleton()->is_low_end()) {
- _set_error("Global const arrays are supported only on high-end platform!");
+ _set_error("Global const arrays are only supported on high-end platform!");
+ return ERR_PARSE_ERROR;
+ }
+ if (constant.array_size > 0 || unknown_size) {
+ _set_error("Array size is already defined!");
return ERR_PARSE_ERROR;
}
-
tk = _get_token();
if (tk.type == TK_BRACKET_CLOSE) {
unknown_size = true;
@@ -8300,7 +8322,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else {
_set_tkpos(prev_pos);
- Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());
+ Node *n = _parse_and_reduce_expression(nullptr, constants);
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
_set_error("Expected single integer constant > 0");
return ERR_PARSE_ERROR;
@@ -8381,7 +8403,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
while (true) {
- Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());
+ Node *n = _parse_and_reduce_expression(nullptr, constants);
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -8436,7 +8458,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
} else {
//variable created with assignment! must parse an expression
- Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo());
+ Node *expr = _parse_and_reduce_expression(nullptr, constants);
if (!expr) {
return ERR_PARSE_ERROR;
}
@@ -8452,7 +8474,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
- if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), 0)) {
+ if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), expr->get_array_size())) {
return ERR_PARSE_ERROR;
}
}
@@ -8483,7 +8505,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
name = tk.text;
- if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
+ if (_find_identifier(nullptr, false, constants, name)) {
_set_error("Redefinition of '" + String(name) + "'");
return ERR_PARSE_ERROR;
}
@@ -8517,6 +8539,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
}
+ if (p_functions.has("constants")) { // Adds global constants: 'PI', 'TAU', 'E'
+ for (const KeyValue<StringName, BuiltInInfo> &E : p_functions["constants"].built_ins) {
+ builtins.built_ins.insert(E.key, E.value);
+ }
+ }
+
ShaderNode::Function function;
function.callable = !p_functions.has(name);
@@ -8532,7 +8560,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
func_node->return_type = type;
func_node->return_struct_name = struct_name;
func_node->return_precision = precision;
- func_node->return_array_size = return_array_size;
+ func_node->return_array_size = array_size;
if (p_functions.has(name)) {
func_node->can_discard = p_functions[name].can_discard;
@@ -8586,7 +8614,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
StringName param_struct_name;
DataPrecision pprecision = PRECISION_DEFAULT;
bool use_precision = false;
- int array_size = 0;
+ int arg_array_size = 0;
if (is_token_precision(tk.type)) {
pprecision = get_token_precision(tk.type);
@@ -8637,9 +8665,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_INT_CONSTANT) {
- array_size = (int)tk.constant;
+ arg_array_size = (int)tk.constant;
- if (array_size > 0) {
+ if (arg_array_size > 0) {
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
_set_error("Expected ']'");
@@ -8691,7 +8719,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- if (array_size > 0) {
+ if (arg_array_size > 0) {
_set_error("Array size is already defined!");
return ERR_PARSE_ERROR;
}
@@ -8699,9 +8727,9 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_INT_CONSTANT) {
- array_size = (int)tk.constant;
+ arg_array_size = (int)tk.constant;
- if (array_size > 0) {
+ if (arg_array_size > 0) {
tk = _get_token();
if (tk.type != TK_BRACKET_CLOSE) {
_set_error("Expected ']'");
@@ -8721,7 +8749,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}
- arg.array_size = array_size;
+ arg.array_size = arg_array_size;
func_node->arguments.push_back(arg);
if (tk.type == TK_COMMA) {
@@ -9067,6 +9095,16 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
}
}
+ if (p_functions.has("constants")) {
+ for (const KeyValue<StringName, BuiltInInfo> &E : p_functions["constants"].built_ins) {
+ ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER;
+ if (E.value.constant) {
+ kind = ScriptCodeCompletionOption::KIND_CONSTANT;
+ }
+ matches.insert(E.key, kind);
+ }
+ }
+
if (skip_function != StringName() && p_functions.has(skip_function)) {
for (const KeyValue<StringName, BuiltInInfo> &E : p_functions[skip_function].built_ins) {
ScriptCodeCompletionOption::Kind kind = ScriptCodeCompletionOption::KIND_MEMBER;
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index c82f71d10d..3de89a89a5 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -287,6 +287,7 @@ public:
OP_CONSTRUCT,
OP_STRUCT,
OP_INDEX,
+ OP_EMPTY,
OP_MAX
};
@@ -989,7 +990,7 @@ private:
bool _check_node_constness(const Node *p_node) const;
Node *_parse_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, int &r_array_size);
- Error _parse_global_array_size(int &r_array_size);
+ Error _parse_global_array_size(int &r_array_size, const FunctionInfo &p_function_info);
Error _parse_local_array_size(BlockNode *p_block, const FunctionInfo &p_function_info, ArrayDeclarationNode *p_node, ArrayDeclarationNode::Declaration *p_decl, int &r_array_size, bool &r_is_unknown_size);
Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index eb5c9e66e8..359196e096 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -59,9 +59,9 @@ ShaderTypes::ShaderTypes() {
/*************** SPATIAL ***********************/
shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3;
@@ -241,9 +241,9 @@ ShaderTypes::ShaderTypes() {
/************ CANVAS ITEM **************************/
shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2;
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["UV"] = ShaderLanguage::TYPE_VEC2;
@@ -334,9 +334,9 @@ ShaderTypes::ShaderTypes() {
/************ PARTICLES **************************/
shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
@@ -367,7 +367,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["LIFETIME"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["DELTA"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["NUMBER"] = constt(ShaderLanguage::TYPE_UINT);
- shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_INT);
+ shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["INDEX"] = constt(ShaderLanguage::TYPE_UINT);
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["EMISSION_TRANSFORM"] = constt(ShaderLanguage::TYPE_MAT4);
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
shader_modes[RS::SHADER_PARTICLES].functions["process"].built_ins["FLAG_EMIT_POSITION"] = constt(ShaderLanguage::TYPE_UINT);
@@ -400,9 +400,9 @@ ShaderTypes::ShaderTypes() {
/************ SKY **************************/
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SKY].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_SKY].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["POSITION"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["RADIANCE"] = constt(ShaderLanguage::TYPE_SAMPLERCUBE);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_HALF_RES_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
@@ -446,9 +446,9 @@ ShaderTypes::ShaderTypes() {
/************ FOG **************************/
shader_modes[RS::SHADER_FOG].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_FOG].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_FOG].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
- shader_modes[RS::SHADER_FOG].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_FOG].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_FOG].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_FOG].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["WORLD_POSITION"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_FOG].functions["fog"].built_ins["OBJECT_POSITION"] = constt(ShaderLanguage::TYPE_VEC3);
diff --git a/servers/rendering/shader_warnings.cpp b/servers/rendering/shader_warnings.cpp
index 0b8476478c..bffae484a8 100644
--- a/servers/rendering/shader_warnings.cpp
+++ b/servers/rendering/shader_warnings.cpp
@@ -61,6 +61,8 @@ String ShaderWarning::get_message() const {
return vformat("The varying '%s' is declared but never used.", subject);
case UNUSED_LOCAL_VARIABLE:
return vformat("The local variable '%s' is declared but never used.", subject);
+ case FORMATTING_ERROR:
+ return subject;
default:
break;
}
@@ -82,6 +84,7 @@ String ShaderWarning::get_name_from_code(Code p_code) {
"UNUSED_UNIFORM",
"UNUSED_VARYING",
"UNUSED_LOCAL_VARIABLE",
+ "FORMATTING_ERROR",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
@@ -110,6 +113,7 @@ static void init_code_to_flags_map() {
code_to_flags_map->insert(ShaderWarning::UNUSED_UNIFORM, ShaderWarning::UNUSED_UNIFORM_FLAG);
code_to_flags_map->insert(ShaderWarning::UNUSED_VARYING, ShaderWarning::UNUSED_VARYING_FLAG);
code_to_flags_map->insert(ShaderWarning::UNUSED_LOCAL_VARIABLE, ShaderWarning::UNUSED_LOCAL_VARIABLE_FLAG);
+ code_to_flags_map->insert(ShaderWarning::FORMATTING_ERROR, ShaderWarning::FORMATTING_ERROR_FLAG);
}
ShaderWarning::CodeFlags ShaderWarning::get_flags_from_codemap(const Map<Code, bool> &p_map) {
diff --git a/servers/rendering/shader_warnings.h b/servers/rendering/shader_warnings.h
index db872d8fb1..18915fffd8 100644
--- a/servers/rendering/shader_warnings.h
+++ b/servers/rendering/shader_warnings.h
@@ -47,6 +47,7 @@ public:
UNUSED_UNIFORM,
UNUSED_VARYING,
UNUSED_LOCAL_VARIABLE,
+ FORMATTING_ERROR,
WARNING_MAX,
};
@@ -59,6 +60,7 @@ public:
UNUSED_UNIFORM_FLAG = 16U,
UNUSED_VARYING_FLAG = 32U,
UNUSED_LOCAL_VARIABLE_FLAG = 64U,
+ FORMATTING_ERROR_FLAG = 128U,
};
private:
diff --git a/tests/servers/test_shader_lang.cpp b/tests/servers/test_shader_lang.cpp
index 0591bf6adf..f4a32c6723 100644
--- a/tests/servers/test_shader_lang.cpp
+++ b/tests/servers/test_shader_lang.cpp
@@ -261,6 +261,8 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
}
code += ")";
break;
+ case SL::OP_EMPTY:
+ break;
default: {
code = "(" + dump_node_code(onode->arguments[0], p_level) + _opstr(onode->op) + dump_node_code(onode->arguments[1], p_level) + ")";
break;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 03e9885bd0..926b5edfaf 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -359,6 +359,19 @@ instead of a combination of distance and attribute errors. Patches for both chan
found in the `patches` directory.
+## minimp3
+
+- Upstream: https://github.com/lieff/minimp3
+- Version: git (afb604c06bc8beb145fecd42c0ceb5bda8795144, 2021)
+- License: CC0 1.0
+
+Files extracted from upstream repository:
+
+- `minimp3.h`
+- `minimp3_ex.h`
+- `LICENSE`
+
+
## miniupnpc
- Upstream: https://github.com/miniupnp/miniupnp
diff --git a/thirdparty/minimp3/minimp3.h b/thirdparty/minimp3/minimp3.h
index 796cbc1f8e..3220ae1a85 100644
--- a/thirdparty/minimp3/minimp3.h
+++ b/thirdparty/minimp3/minimp3.h
@@ -881,12 +881,22 @@ static void L3_midside_stereo(float *left, int n)
int i = 0;
float *right = left + 576;
#if HAVE_SIMD
- if (have_simd()) for (; i < n - 3; i += 4)
+ if (have_simd())
{
- f4 vl = VLD(left + i);
- f4 vr = VLD(right + i);
- VSTORE(left + i, VADD(vl, vr));
- VSTORE(right + i, VSUB(vl, vr));
+ for (; i < n - 3; i += 4)
+ {
+ f4 vl = VLD(left + i);
+ f4 vr = VLD(right + i);
+ VSTORE(left + i, VADD(vl, vr));
+ VSTORE(right + i, VSUB(vl, vr));
+ }
+#ifdef __GNUC__
+ /* Workaround for spurious -Waggressive-loop-optimizations warning from gcc.
+ * For more info see: https://github.com/lieff/minimp3/issues/88
+ */
+ if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
+ return;
+#endif
}
#endif /* HAVE_SIMD */
for (; i < n; i++)
@@ -1353,7 +1363,7 @@ static void mp3d_DCT_II(float *grbuf, int n)
} else
#endif /* HAVE_SIMD */
#ifdef MINIMP3_ONLY_SIMD
- {}
+ {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
#else /* MINIMP3_ONLY_SIMD */
for (; k < n; k++)
{
@@ -1583,7 +1593,7 @@ static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins)
} else
#endif /* HAVE_SIMD */
#ifdef MINIMP3_ONLY_SIMD
- {}
+ {} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
#else /* MINIMP3_ONLY_SIMD */
for (i = 14; i >= 0; i--)
{
diff --git a/thirdparty/minimp3/minimp3_ex.h b/thirdparty/minimp3/minimp3_ex.h
index e29dd15b2e..2871705df3 100644
--- a/thirdparty/minimp3/minimp3_ex.h
+++ b/thirdparty/minimp3/minimp3_ex.h
@@ -6,6 +6,7 @@
This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
+#include <stddef.h>
#include "minimp3.h"
/* flags for mp3dec_ex_open_* functions */
@@ -128,8 +129,10 @@ int mp3dec_ex_open_w(mp3dec_ex_t *dec, const wchar_t *file_name, int flags);
#endif
#endif /*MINIMP3_EXT_H*/
-#ifdef MINIMP3_IMPLEMENTATION
+#if defined(MINIMP3_IMPLEMENTATION) && !defined(_MINIMP3_EX_IMPLEMENTATION_GUARD)
+#define _MINIMP3_EX_IMPLEMENTATION_GUARD
#include <limits.h>
+#include "minimp3.h"
static void mp3dec_skip_id3v1(const uint8_t *buf, size_t *pbuf_size)
{
@@ -1391,4 +1394,4 @@ void mp3dec_ex_close(mp3dec_ex_t *dec)
}
#endif
-#endif /*MINIMP3_IMPLEMENTATION*/
+#endif /* MINIMP3_IMPLEMENTATION && !_MINIMP3_EX_IMPLEMENTATION_GUARD */