summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/classes/FontFile.xml4
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/TextServer.xml12
-rw-r--r--doc/classes/TextServerExtension.xml431
-rw-r--r--drivers/d3d12/rendering_context_driver_d3d12.h2
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp2
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.h2
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp10
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp8
-rw-r--r--drivers/gles3/shader_gles3.cpp4
-rw-r--r--drivers/gles3/storage/texture_storage.cpp6
-rw-r--r--editor/editor_file_system.cpp8
-rw-r--r--editor/export/editor_export_platform.cpp5
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/icons/FadeCross.svg1
-rw-r--r--editor/icons/FadeDisabled.svg1
-rw-r--r--editor/icons/FadeIn.svg1
-rw-r--r--editor/icons/FadeOut.svg1
-rw-r--r--editor/import/resource_importer_bmfont.cpp18
-rw-r--r--editor/import_dock.cpp58
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp2
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp2
-rw-r--r--main/main.cpp28
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp97
-rw-r--r--modules/gdscript/gdscript_parser.cpp316
-rw-r--r--modules/gdscript/gdscript_parser.h23
-rw-r--r--modules/gdscript/gdscript_warning.cpp36
-rw-r--r--modules/gdscript/gdscript_warning.h20
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.gd35
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out29
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd156
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.out1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/duplicate_tool.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/signal_declaration.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.out10
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info.gd10
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd4
-rw-r--r--modules/interactive_music/SCsub11
-rw-r--r--modules/interactive_music/audio_stream_interactive.cpp1030
-rw-r--r--modules/interactive_music/audio_stream_interactive.h270
-rw-r--r--modules/interactive_music/audio_stream_playlist.cpp406
-rw-r--r--modules/interactive_music/audio_stream_playlist.h125
-rw-r--r--modules/interactive_music/audio_stream_synchronized.cpp312
-rw-r--r--modules/interactive_music/audio_stream_synchronized.h119
-rw-r--r--modules/interactive_music/config.py21
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamInteractive.xml229
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamPlaybackInteractive.xml27
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamPlaybackPlaylist.xml10
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamPlaybackSynchronized.xml9
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamPlaylist.xml52
-rw-r--r--modules/interactive_music/doc_classes/AudioStreamSynchronized.xml53
-rw-r--r--modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp416
-rw-r--r--modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h110
-rw-r--r--modules/interactive_music/register_types.cpp60
-rw-r--r--modules/interactive_music/register_types.h39
-rw-r--r--modules/ktx/texture_loader_ktx.cpp2
-rw-r--r--modules/mono/editor/hostfxr_resolver.cpp2
-rw-r--r--modules/msdfgen/SCsub3
-rw-r--r--modules/openxr/openxr_api.h2
-rw-r--r--modules/text_server_adv/SCsub1
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct4
-rw-r--r--modules/text_server_adv/text_server_adv.cpp12
-rw-r--r--modules/text_server_fb/SCsub1
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct2
-rw-r--r--modules/text_server_fb/text_server_fb.cpp18
-rw-r--r--platform/ios/os_ios.mm2
-rw-r--r--platform/linuxbsd/wayland/detect_prime_egl.cpp2
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp8
-rw-r--r--platform/macos/display_server_macos.mm2
-rw-r--r--platform/macos/os_macos.mm2
-rw-r--r--platform/windows/crash_handler_windows.cpp2
-rw-r--r--platform/windows/display_server_windows.cpp76
-rw-r--r--platform/windows/gl_manager_windows_native.cpp20
-rw-r--r--platform/windows/os_windows.cpp28
-rw-r--r--platform/windows/tts_windows.cpp4
-rw-r--r--platform/windows/wgl_detect_version.cpp2
-rw-r--r--scene/resources/font.cpp11
-rw-r--r--scene/resources/font.h2
-rw-r--r--scene/resources/material.cpp6
-rw-r--r--servers/SCsub22
-rw-r--r--servers/register_server_types.cpp60
-rw-r--r--servers/rendering/renderer_compositor.cpp2
-rw-r--r--servers/rendering/renderer_rd/effects/vrs.cpp5
-rw-r--r--servers/rendering/renderer_scene_cull.h3
-rw-r--r--servers/rendering/renderer_viewport.cpp14
-rw-r--r--servers/rendering/renderer_viewport.h5
-rw-r--r--servers/rendering/rendering_method.h7
-rw-r--r--servers/rendering/rendering_server_default.cpp2
-rw-r--r--servers/text/text_server_dummy.h84
-rw-r--r--servers/text/text_server_extension.cpp182
-rw-r--r--servers/text_server.cpp42
-rw-r--r--servers/text_server.h6
-rw-r--r--thirdparty/README.md4
-rw-r--r--thirdparty/icu4c/common/unicode/uvernum.h6
-rw-r--r--thirdparty/icu4c/icudt74l.datbin4419920 -> 4419952 bytes
-rw-r--r--thirdparty/msdfgen/LICENSE.txt2
-rw-r--r--thirdparty/msdfgen/core/Bitmap.h10
-rw-r--r--thirdparty/msdfgen/core/Bitmap.hpp10
-rw-r--r--thirdparty/msdfgen/core/BitmapRef.hpp8
-rw-r--r--thirdparty/msdfgen/core/Contour.cpp2
-rw-r--r--thirdparty/msdfgen/core/Contour.h2
-rw-r--r--thirdparty/msdfgen/core/EdgeColor.h2
-rw-r--r--thirdparty/msdfgen/core/EdgeHolder.cpp12
-rw-r--r--thirdparty/msdfgen/core/EdgeHolder.h12
-rw-r--r--thirdparty/msdfgen/core/MSDFErrorCorrection.cpp8
-rw-r--r--thirdparty/msdfgen/core/Projection.h2
-rw-r--r--thirdparty/msdfgen/core/Scanline.cpp2
-rw-r--r--thirdparty/msdfgen/core/Scanline.h1
-rw-r--r--thirdparty/msdfgen/core/Shape.cpp41
-rw-r--r--thirdparty/msdfgen/core/Shape.h2
-rw-r--r--thirdparty/msdfgen/core/ShapeDistanceFinder.h2
-rw-r--r--thirdparty/msdfgen/core/ShapeDistanceFinder.hpp6
-rw-r--r--thirdparty/msdfgen/core/SignedDistance.cpp29
-rw-r--r--thirdparty/msdfgen/core/SignedDistance.h23
-rw-r--r--thirdparty/msdfgen/core/SignedDistance.hpp38
-rw-r--r--thirdparty/msdfgen/core/Vector2.cpp146
-rw-r--r--thirdparty/msdfgen/core/Vector2.h66
-rw-r--r--thirdparty/msdfgen/core/Vector2.hpp167
-rw-r--r--thirdparty/msdfgen/core/arithmetics.hpp2
-rw-r--r--thirdparty/msdfgen/core/base.h16
-rw-r--r--thirdparty/msdfgen/core/bitmap-interpolation.hpp2
-rw-r--r--thirdparty/msdfgen/core/contour-combiners.cpp4
-rw-r--r--thirdparty/msdfgen/core/contour-combiners.h4
-rw-r--r--thirdparty/msdfgen/core/edge-coloring.cpp11
-rw-r--r--thirdparty/msdfgen/core/edge-segments.cpp32
-rw-r--r--thirdparty/msdfgen/core/edge-segments.h36
-rw-r--r--thirdparty/msdfgen/core/edge-selectors.h4
-rw-r--r--thirdparty/msdfgen/core/equation-solver.cpp2
-rw-r--r--thirdparty/msdfgen/core/equation-solver.h2
-rw-r--r--thirdparty/msdfgen/core/generator-config.h1
-rw-r--r--thirdparty/msdfgen/core/msdf-error-correction.h2
-rw-r--r--thirdparty/msdfgen/core/pixel-conversion.hpp2
-rw-r--r--thirdparty/msdfgen/core/rasterization.h2
-rw-r--r--thirdparty/msdfgen/core/render-sdf.h2
-rw-r--r--thirdparty/msdfgen/core/sdf-error-estimation.h2
-rw-r--r--thirdparty/msdfgen/core/shape-description.cpp59
-rw-r--r--thirdparty/msdfgen/core/shape-description.h1
-rw-r--r--thirdparty/msdfgen/msdfgen.h5
149 files changed, 5275 insertions, 886 deletions
diff --git a/doc/classes/FontFile.xml b/doc/classes/FontFile.xml
index 424f1931ff..1b8fa00772 100644
--- a/doc/classes/FontFile.xml
+++ b/doc/classes/FontFile.xml
@@ -42,7 +42,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="size" type="Vector2i" />
<description>
- Removes all rendered glyphs information from the cache entry.
+ Removes all rendered glyph information from the cache entry.
[b]Note:[/b] This function will not remove textures associated with the glyphs, use [method remove_texture] to remove them manually.
</description>
</method>
@@ -579,7 +579,7 @@
<param index="0" name="cache_index" type="int" />
<param index="1" name="transform" type="Transform2D" />
<description>
- Sets 2D transform, applied to the font outlines, can be used for slanting, flipping and rotating glyphs.
+ Sets 2D transform, applied to the font outlines, can be used for slanting, flipping, and rotating glyphs.
</description>
</method>
<method name="set_variation_coordinates">
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 6e864316f1..9433abf0d4 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -470,11 +470,12 @@
<member name="debug/gdscript/warnings/confusable_local_usage" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
</member>
- <member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1">
+ <member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1" deprecated="This warning is never produced. Instead, an error is generated if the expression type is known at compile time.">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
</member>
<member name="debug/gdscript/warnings/deprecated_keyword" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when deprecated keywords are used.
+ [b]Note:[/b] There are currently no deprecated keywords, so this warning is never produced.
</member>
<member name="debug/gdscript/warnings/empty_file" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an empty file is parsed.
@@ -485,7 +486,7 @@
<member name="debug/gdscript/warnings/exclude_addons" type="bool" setter="" getter="" default="true">
If [code]true[/code], scripts in the [code]res://addons[/code] folder will not generate warnings.
</member>
- <member name="debug/gdscript/warnings/function_used_as_property" type="int" setter="" getter="" default="1">
+ <member name="debug/gdscript/warnings/function_used_as_property" type="int" setter="" getter="" default="1" deprecated="This warning is never produced. When a function is used as a property, a [Callable] is returned.">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when using a function as if it is a property.
</member>
<member name="debug/gdscript/warnings/get_node_default_without_onready" type="int" setter="" getter="" default="2">
@@ -519,7 +520,7 @@
<member name="debug/gdscript/warnings/onready_with_export" type="int" setter="" getter="" default="2">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when the [code]@onready[/code] annotation is used together with the [code]@export[/code] annotation, since it may not behave as expected.
</member>
- <member name="debug/gdscript/warnings/property_used_as_function" type="int" setter="" getter="" default="1">
+ <member name="debug/gdscript/warnings/property_used_as_function" type="int" setter="" getter="" default="1" deprecated="This warning is never produced. Instead, an error is generated if the expression type is known at compile time.">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when using a property as if it is a function.
</member>
<member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1">
@@ -593,7 +594,7 @@
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a private member variable is never used.
</member>
<member name="debug/gdscript/warnings/unused_signal" type="int" setter="" getter="" default="1">
- When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a signal is declared but never emitted.
+ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a signal is declared but never explicitly used in the class.
</member>
<member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 4102187350..cd70316aa9 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -27,7 +27,7 @@
<param index="0" name="direction" type="int" enum="TextServer.Direction" default="0" />
<param index="1" name="orientation" type="int" enum="TextServer.Orientation" default="0" />
<description>
- Creates new buffer for complex text layout, with the given [param direction] and [param orientation]. To free the resulting buffer, use [method free_rid] method.
+ Creates a new buffer for complex text layout, with the given [param direction] and [param orientation]. To free the resulting buffer, use [method free_rid] method.
[b]Note:[/b] Direction is ignored if server does not support [constant FEATURE_BIDI_LAYOUT] feature (supported by [TextServerAdvanced]).
[b]Note:[/b] Orientation is ignored if server does not support [constant FEATURE_VERTICAL_LAYOUT] feature (supported by [TextServerAdvanced]).
</description>
@@ -48,7 +48,7 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
- Removes all rendered glyphs information from the cache entry.
+ Removes all rendered glyph information from the cache entry.
[b]Note:[/b] This function will not remove textures associated with the glyphs, use [method font_remove_texture] to remove them manually.
</description>
</method>
@@ -956,7 +956,7 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="transform" type="Transform2D" />
<description>
- Sets 2D transform, applied to the font outlines, can be used for slanting, flipping and rotating glyphs.
+ Sets 2D transform, applied to the font outlines, can be used for slanting, flipping, and rotating glyphs.
For example, to simulate italic typeface by slanting, apply the following transform [code]Transform2D(1.0, slant, 0.0, 1.0, 0.0, 0.0)[/code].
</description>
</method>
@@ -1115,7 +1115,7 @@
<return type="int" />
<param index="0" name="name" type="String" />
<description>
- Converts readable feature, variation, script or language name to OpenType tag.
+ Converts readable feature, variation, script, or language name to OpenType tag.
</description>
</method>
<method name="parse_number" qualifiers="const">
@@ -1173,7 +1173,7 @@
<param index="3" name="size" type="int" />
<param index="4" name="opentype_features" type="Dictionary" default="{}" />
<description>
- Changes text span font, font size and OpenType features, without changing the text.
+ Changes text span font, font size, and OpenType features, without changing the text.
</description>
</method>
<method name="shaped_text_add_object">
@@ -1758,7 +1758,7 @@
<return type="String" />
<param index="0" name="tag" type="int" />
<description>
- Converts OpenType tag to readable feature, variation, script or language name.
+ Converts OpenType tag to readable feature, variation, script, or language name.
</description>
</method>
</methods>
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index aa9062b844..9b7fc42ddf 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -12,17 +12,23 @@
<method name="_cleanup" qualifiers="virtual">
<return type="void" />
<description>
+ [b]Optional.[/b]
+ This method is called before text server is unregistered.
</description>
</method>
<method name="_create_font" qualifiers="virtual">
<return type="RID" />
<description>
+ [b]Required.[/b]
+ Creates a new, empty font cache entry resource.
</description>
</method>
<method name="_create_font_linked_variation" qualifiers="virtual">
<return type="RID" />
<param index="0" name="font_rid" type="RID" />
<description>
+ Optional, implement if font supports extra spacing or baseline offset.
+ Creates a new variation existing font which is reusing the same glyph cache and font data.
</description>
</method>
<method name="_create_shaped_text" qualifiers="virtual">
@@ -30,6 +36,8 @@
<param index="0" name="direction" type="int" enum="TextServer.Direction" />
<param index="1" name="orientation" type="int" enum="TextServer.Orientation" />
<description>
+ [b]Required.[/b]
+ Creates a new buffer for complex text layout, with the given [param direction] and [param orientation].
</description>
</method>
<method name="_draw_hex_code_box" qualifiers="virtual const">
@@ -40,6 +48,8 @@
<param index="3" name="index" type="int" />
<param index="4" name="color" type="Color" />
<description>
+ [b]Optional.[/b]
+ Draws box displaying character hexadecimal code.
</description>
</method>
<method name="_font_clear_glyphs" qualifiers="virtual">
@@ -47,6 +57,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
+ [b]Required.[/b]
+ Removes all rendered glyph information from the cache entry.
</description>
</method>
<method name="_font_clear_kerning_map" qualifiers="virtual">
@@ -54,12 +66,16 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Optional.[/b]
+ Removes all kerning overrides.
</description>
</method>
<method name="_font_clear_size_cache" qualifiers="virtual">
<return type="void" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Removes all font sizes from the cache entry.
</description>
</method>
<method name="_font_clear_textures" qualifiers="virtual">
@@ -67,6 +83,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
+ [b]Required.[/b]
+ Removes all textures from font cache entry.
</description>
</method>
<method name="_font_draw_glyph" qualifiers="virtual const">
@@ -78,6 +96,8 @@
<param index="4" name="index" type="int" />
<param index="5" name="color" type="Color" />
<description>
+ [b]Required.[/b]
+ Draws single glyph into a canvas item at the position, using [param font_rid] at the size [param size].
</description>
</method>
<method name="_font_draw_glyph_outline" qualifiers="virtual const">
@@ -90,12 +110,16 @@
<param index="5" name="index" type="int" />
<param index="6" name="color" type="Color" />
<description>
+ [b]Required.[/b]
+ Draws single glyph outline of size [param outline_size] into a canvas item at the position, using [param font_rid] at the size [param size].
</description>
</method>
<method name="_font_get_antialiasing" qualifiers="virtual const">
<return type="int" enum="TextServer.FontAntialiasing" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font anti-aliasing mode.
</description>
</method>
<method name="_font_get_ascent" qualifiers="virtual const">
@@ -103,12 +127,16 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Required.[/b]
+ Returns the font ascent (number of pixels above the baseline).
</description>
</method>
<method name="_font_get_baseline_offset" qualifiers="virtual const">
<return type="float" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns extra baseline offset (as a fraction of font height).
</description>
</method>
<method name="_font_get_char_from_glyph_index" qualifiers="virtual const">
@@ -117,6 +145,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="glyph_index" type="int" />
<description>
+ [b]Required.[/b]
+ Returns character code associated with [param glyph_index], or [code]0[/code] if [param glyph_index] is invalid.
</description>
</method>
<method name="_font_get_descent" qualifiers="virtual const">
@@ -124,53 +154,71 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Required.[/b]
+ Returns the font descent (number of pixels below the baseline).
</description>
</method>
<method name="_font_get_disable_embedded_bitmaps" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns whether the font's embedded bitmap loading is disabled.
</description>
</method>
<method name="_font_get_embolden" qualifiers="virtual const">
<return type="float" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font embolden strength.
</description>
</method>
<method name="_font_get_face_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns number of faces in the TrueType / OpenType collection.
</description>
</method>
<method name="_font_get_face_index" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns an active face index in the TrueType / OpenType collection.
</description>
</method>
<method name="_font_get_fixed_size" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns bitmap font fixed size.
</description>
</method>
<method name="_font_get_fixed_size_scale_mode" qualifiers="virtual const">
<return type="int" enum="TextServer.FixedSizeScaleMode" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns bitmap font scaling mode.
</description>
</method>
<method name="_font_get_generate_mipmaps" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if font texture mipmap generation is enabled.
</description>
</method>
<method name="_font_get_global_oversampling" qualifiers="virtual const">
<return type="float" />
<description>
+ [b]Optional.[/b]
+ Returns the font oversampling factor, shared by all fonts in the TextServer.
</description>
</method>
<method name="_font_get_glyph_advance" qualifiers="virtual const">
@@ -179,6 +227,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns glyph advance (offset of the next glyph).
</description>
</method>
<method name="_font_get_glyph_contours" qualifiers="virtual const">
@@ -187,6 +237,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="index" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns outline contours of the glyph.
</description>
</method>
<method name="_font_get_glyph_index" qualifiers="virtual const">
@@ -196,6 +248,8 @@
<param index="2" name="char" type="int" />
<param index="3" name="variation_selector" type="int" />
<description>
+ [b]Required.[/b]
+ Returns the glyph index of a [param char], optionally modified by the [param variation_selector].
</description>
</method>
<method name="_font_get_glyph_list" qualifiers="virtual const">
@@ -203,6 +257,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
+ [b]Required.[/b]
+ Returns list of rendered glyphs in the cache entry.
</description>
</method>
<method name="_font_get_glyph_offset" qualifiers="virtual const">
@@ -211,6 +267,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns glyph offset from the baseline.
</description>
</method>
<method name="_font_get_glyph_size" qualifiers="virtual const">
@@ -219,6 +277,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns size of the glyph.
</description>
</method>
<method name="_font_get_glyph_texture_idx" qualifiers="virtual const">
@@ -227,6 +287,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns index of the cache texture containing the glyph.
</description>
</method>
<method name="_font_get_glyph_texture_rid" qualifiers="virtual const">
@@ -235,6 +297,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns resource ID of the cache texture containing the glyph.
</description>
</method>
<method name="_font_get_glyph_texture_size" qualifiers="virtual const">
@@ -243,6 +307,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns size of the cache texture containing the glyph.
</description>
</method>
<method name="_font_get_glyph_uv_rect" qualifiers="virtual const">
@@ -251,12 +317,16 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Returns rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="_font_get_hinting" qualifiers="virtual const">
<return type="int" enum="TextServer.Hinting" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns the font hinting mode. Used by dynamic fonts only.
</description>
</method>
<method name="_font_get_kerning" qualifiers="virtual const">
@@ -265,6 +335,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="glyph_pair" type="Vector2i" />
<description>
+ [b]Optional.[/b]
+ Returns kerning for the pair of glyphs.
</description>
</method>
<method name="_font_get_kerning_list" qualifiers="virtual const">
@@ -272,6 +344,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns list of the kerning overrides.
</description>
</method>
<method name="_font_get_language_support_override" qualifiers="virtual">
@@ -279,48 +353,64 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if support override is enabled for the [param language].
</description>
</method>
<method name="_font_get_language_support_overrides" qualifiers="virtual">
<return type="PackedStringArray" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns list of language support overrides.
</description>
</method>
<method name="_font_get_msdf_pixel_range" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns the width of the range around the shape between the minimum and maximum representable signed distance.
</description>
</method>
<method name="_font_get_msdf_size" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns source font size used to generate MSDF textures.
</description>
</method>
<method name="_font_get_name" qualifiers="virtual const">
<return type="String" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font family name.
</description>
</method>
<method name="_font_get_opentype_feature_overrides" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font OpenType feature set override.
</description>
</method>
<method name="_font_get_ot_name_strings" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [Dictionary] with OpenType font name strings (localized font names, version, description, license information, sample text, etc.).
</description>
</method>
<method name="_font_get_oversampling" qualifiers="virtual const">
<return type="float" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
</description>
</method>
<method name="_font_get_scale" qualifiers="virtual const">
@@ -328,6 +418,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Required.[/b]
+ Returns scaling factor of the color bitmap font.
</description>
</method>
<method name="_font_get_script_support_override" qualifiers="virtual">
@@ -335,18 +427,24 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="script" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if support override is enabled for the [param script].
</description>
</method>
<method name="_font_get_script_support_overrides" qualifiers="virtual">
<return type="PackedStringArray" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns list of script support overrides.
</description>
</method>
<method name="_font_get_size_cache_list" qualifiers="virtual const">
<return type="Vector2i[]" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns list of the font sizes in the cache. Each size is [Vector2i] with font size and outline size.
</description>
</method>
<method name="_font_get_spacing" qualifiers="virtual const">
@@ -354,36 +452,48 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
<description>
+ [b]Optional.[/b]
+ Returns the spacing for [param spacing] (see [enum TextServer.SpacingType]) in pixels (not relative to the font size).
</description>
</method>
<method name="_font_get_stretch" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font stretch amount, compared to a normal width. A percentage value between [code]50%[/code] and [code]200%[/code].
</description>
</method>
<method name="_font_get_style" qualifiers="virtual const">
<return type="int" enum="TextServer.FontStyle" is_bitfield="true" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="_font_get_style_name" qualifiers="virtual const">
<return type="String" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font style name.
</description>
</method>
<method name="_font_get_subpixel_positioning" qualifiers="virtual const">
<return type="int" enum="TextServer.SubpixelPositioning" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns font subpixel glyph positioning mode.
</description>
</method>
<method name="_font_get_supported_chars" qualifiers="virtual const">
<return type="String" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns a string containing all the characters available in the font.
</description>
</method>
<method name="_font_get_texture_count" qualifiers="virtual const">
@@ -391,6 +501,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
+ [b]Required.[/b]
+ Returns number of textures used by font cache entry.
</description>
</method>
<method name="_font_get_texture_image" qualifiers="virtual const">
@@ -399,6 +511,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="texture_index" type="int" />
<description>
+ [b]Required.[/b]
+ Returns font cache texture image data.
</description>
</method>
<method name="_font_get_texture_offsets" qualifiers="virtual const">
@@ -407,12 +521,16 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="texture_index" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns array containing glyph packing data.
</description>
</method>
<method name="_font_get_transform" qualifiers="virtual const">
<return type="Transform2D" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns 2D transform applied to the font outlines.
</description>
</method>
<method name="_font_get_underline_position" qualifiers="virtual const">
@@ -420,6 +538,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Required.[/b]
+ Returns pixel offset of the underline below the baseline.
</description>
</method>
<method name="_font_get_underline_thickness" qualifiers="virtual const">
@@ -427,18 +547,24 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="int" />
<description>
+ [b]Required.[/b]
+ Returns thickness of the underline in pixels.
</description>
</method>
<method name="_font_get_variation_coordinates" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns variation coordinates for the specified font cache entry.
</description>
</method>
<method name="_font_get_weight" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns weight (boldness) of the font. A value in the [code]100...999[/code] range, normal font weight is [code]400[/code], bold font weight is [code]700[/code].
</description>
</method>
<method name="_font_has_char" qualifiers="virtual const">
@@ -446,18 +572,24 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="char" type="int" />
<description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if a Unicode [param char] is available in the font.
</description>
</method>
<method name="_font_is_allow_system_fallback" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if system fonts can be automatically used as fallbacks.
</description>
</method>
<method name="_font_is_force_autohinter" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if auto-hinting is supported and preferred over font built-in hinting.
</description>
</method>
<method name="_font_is_language_supported" qualifiers="virtual const">
@@ -465,12 +597,16 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code], if font supports given language ([url=https://en.wikipedia.org/wiki/ISO_639-1]ISO 639[/url] code).
</description>
</method>
<method name="_font_is_multichannel_signed_distance_field" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
</description>
</method>
<method name="_font_is_script_supported" qualifiers="virtual const">
@@ -478,6 +614,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="script" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code], if font supports given script (ISO 15924 code).
</description>
</method>
<method name="_font_remove_glyph" qualifiers="virtual">
@@ -486,6 +624,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="glyph" type="int" />
<description>
+ [b]Required.[/b]
+ Removes specified rendered glyph information from the cache entry.
</description>
</method>
<method name="_font_remove_kerning" qualifiers="virtual">
@@ -494,6 +634,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="glyph_pair" type="Vector2i" />
<description>
+ [b]Optional.[/b]
+ Removes kerning override for the pair of glyphs.
</description>
</method>
<method name="_font_remove_language_support_override" qualifiers="virtual">
@@ -501,6 +643,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Remove language support override.
</description>
</method>
<method name="_font_remove_script_support_override" qualifiers="virtual">
@@ -508,6 +652,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="script" type="String" />
<description>
+ [b]Optional.[/b]
+ Removes script support override.
</description>
</method>
<method name="_font_remove_size_cache" qualifiers="virtual">
@@ -515,6 +661,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="size" type="Vector2i" />
<description>
+ [b]Required.[/b]
+ Removes specified font size from the cache entry.
</description>
</method>
<method name="_font_remove_texture" qualifiers="virtual">
@@ -523,6 +671,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="texture_index" type="int" />
<description>
+ [b]Required.[/b]
+ Removes specified texture from the cache entry.
</description>
</method>
<method name="_font_render_glyph" qualifiers="virtual">
@@ -531,6 +681,8 @@
<param index="1" name="size" type="Vector2i" />
<param index="2" name="index" type="int" />
<description>
+ [b]Optional.[/b]
+ Renders specified glyph to the font cache texture.
</description>
</method>
<method name="_font_render_range" qualifiers="virtual">
@@ -540,6 +692,8 @@
<param index="2" name="start" type="int" />
<param index="3" name="end" type="int" />
<description>
+ [b]Optional.[/b]
+ Renders the range of characters to the font cache texture.
</description>
</method>
<method name="_font_set_allow_system_fallback" qualifiers="virtual">
@@ -547,6 +701,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="allow_system_fallback" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code], system fonts can be automatically used as fallbacks.
</description>
</method>
<method name="_font_set_antialiasing" qualifiers="virtual">
@@ -554,6 +710,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="antialiasing" type="int" enum="TextServer.FontAntialiasing" />
<description>
+ [b]Optional.[/b]
+ Sets font anti-aliasing mode.
</description>
</method>
<method name="_font_set_ascent" qualifiers="virtual">
@@ -562,6 +720,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="ascent" type="float" />
<description>
+ [b]Required.[/b]
+ Sets the font ascent (number of pixels above the baseline).
</description>
</method>
<method name="_font_set_baseline_offset" qualifiers="virtual">
@@ -569,6 +729,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="baseline_offset" type="float" />
<description>
+ [b]Optional.[/b]
+ Sets extra baseline offset (as a fraction of font height).
</description>
</method>
<method name="_font_set_data" qualifiers="virtual">
@@ -576,6 +738,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="data" type="PackedByteArray" />
<description>
+ [b]Optional.[/b]
+ Sets font source data, e.g contents of the dynamic font source file.
</description>
</method>
<method name="_font_set_data_ptr" qualifiers="virtual">
@@ -584,6 +748,8 @@
<param index="1" name="data_ptr" type="const uint8_t*" />
<param index="2" name="data_size" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets pointer to the font source data, e.g contents of the dynamic font source file.
</description>
</method>
<method name="_font_set_descent" qualifiers="virtual">
@@ -592,6 +758,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="descent" type="float" />
<description>
+ [b]Required.[/b]
+ Sets the font descent (number of pixels below the baseline).
</description>
</method>
<method name="_font_set_disable_embedded_bitmaps" qualifiers="virtual">
@@ -599,6 +767,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="disable_embedded_bitmaps" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code], embedded font bitmap loading is disabled.
</description>
</method>
<method name="_font_set_embolden" qualifiers="virtual">
@@ -606,6 +776,7 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="strength" type="float" />
<description>
+ Sets font embolden strength. If [param strength] is not equal to zero, emboldens the font outlines. Negative values reduce the outline thickness.
</description>
</method>
<method name="_font_set_face_index" qualifiers="virtual">
@@ -613,6 +784,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="face_index" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets an active face index in the TrueType / OpenType collection.
</description>
</method>
<method name="_font_set_fixed_size" qualifiers="virtual">
@@ -620,6 +793,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="fixed_size" type="int" />
<description>
+ [b]Required.[/b]
+ Sets bitmap font fixed size. If set to value greater than zero, same cache entry will be used for all font sizes.
</description>
</method>
<method name="_font_set_fixed_size_scale_mode" qualifiers="virtual">
@@ -627,6 +802,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="fixed_size_scale_mode" type="int" enum="TextServer.FixedSizeScaleMode" />
<description>
+ [b]Required.[/b]
+ Sets bitmap font scaling mode. This property is used only if [code]fixed_size[/code] is greater than zero.
</description>
</method>
<method name="_font_set_force_autohinter" qualifiers="virtual">
@@ -634,6 +811,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="force_autohinter" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code] auto-hinting is preferred over font built-in hinting.
</description>
</method>
<method name="_font_set_generate_mipmaps" qualifiers="virtual">
@@ -641,12 +820,16 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="generate_mipmaps" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code] font texture mipmap generation is enabled.
</description>
</method>
<method name="_font_set_global_oversampling" qualifiers="virtual">
<return type="void" />
<param index="0" name="oversampling" type="float" />
<description>
+ [b]Optional.[/b]
+ Sets oversampling factor, shared by all font in the TextServer.
</description>
</method>
<method name="_font_set_glyph_advance" qualifiers="virtual">
@@ -656,6 +839,8 @@
<param index="2" name="glyph" type="int" />
<param index="3" name="advance" type="Vector2" />
<description>
+ [b]Required.[/b]
+ Sets glyph advance (offset of the next glyph).
</description>
</method>
<method name="_font_set_glyph_offset" qualifiers="virtual">
@@ -665,6 +850,8 @@
<param index="2" name="glyph" type="int" />
<param index="3" name="offset" type="Vector2" />
<description>
+ [b]Required.[/b]
+ Sets glyph offset from the baseline.
</description>
</method>
<method name="_font_set_glyph_size" qualifiers="virtual">
@@ -674,6 +861,8 @@
<param index="2" name="glyph" type="int" />
<param index="3" name="gl_size" type="Vector2" />
<description>
+ [b]Required.[/b]
+ Sets size of the glyph.
</description>
</method>
<method name="_font_set_glyph_texture_idx" qualifiers="virtual">
@@ -683,6 +872,8 @@
<param index="2" name="glyph" type="int" />
<param index="3" name="texture_idx" type="int" />
<description>
+ [b]Required.[/b]
+ Sets index of the cache texture containing the glyph.
</description>
</method>
<method name="_font_set_glyph_uv_rect" qualifiers="virtual">
@@ -692,6 +883,8 @@
<param index="2" name="glyph" type="int" />
<param index="3" name="uv_rect" type="Rect2" />
<description>
+ [b]Required.[/b]
+ Sets rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="_font_set_hinting" qualifiers="virtual">
@@ -699,6 +892,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="hinting" type="int" enum="TextServer.Hinting" />
<description>
+ [b]Optional.[/b]
+ Sets font hinting mode. Used by dynamic fonts only.
</description>
</method>
<method name="_font_set_kerning" qualifiers="virtual">
@@ -708,6 +903,8 @@
<param index="2" name="glyph_pair" type="Vector2i" />
<param index="3" name="kerning" type="Vector2" />
<description>
+ [b]Optional.[/b]
+ Sets kerning for the pair of glyphs.
</description>
</method>
<method name="_font_set_language_support_override" qualifiers="virtual">
@@ -716,6 +913,8 @@
<param index="1" name="language" type="String" />
<param index="2" name="supported" type="bool" />
<description>
+ [b]Optional.[/b]
+ Adds override for [method _font_is_language_supported].
</description>
</method>
<method name="_font_set_msdf_pixel_range" qualifiers="virtual">
@@ -723,6 +922,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="msdf_pixel_range" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets the width of the range around the shape between the minimum and maximum representable signed distance.
</description>
</method>
<method name="_font_set_msdf_size" qualifiers="virtual">
@@ -730,6 +931,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="msdf_size" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets source font size used to generate MSDF textures.
</description>
</method>
<method name="_font_set_multichannel_signed_distance_field" qualifiers="virtual">
@@ -737,6 +940,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="msdf" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data. MSDF rendering allows displaying the font at any scaling factor without blurriness, and without incurring a CPU cost when the font size changes (since the font no longer needs to be rasterized on the CPU). As a downside, font hinting is not available with MSDF. The lack of font hinting may result in less crisp and less readable fonts at small sizes.
</description>
</method>
<method name="_font_set_name" qualifiers="virtual">
@@ -744,6 +949,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="name" type="String" />
<description>
+ [b]Optional.[/b]
+ Sets the font family name.
</description>
</method>
<method name="_font_set_opentype_feature_overrides" qualifiers="virtual">
@@ -751,6 +958,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="overrides" type="Dictionary" />
<description>
+ [b]Optional.[/b]
+ Sets font OpenType feature set override.
</description>
</method>
<method name="_font_set_oversampling" qualifiers="virtual">
@@ -758,6 +967,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="oversampling" type="float" />
<description>
+ [b]Optional.[/b]
+ Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
</description>
</method>
<method name="_font_set_scale" qualifiers="virtual">
@@ -766,6 +977,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="scale" type="float" />
<description>
+ [b]Required.[/b]
+ Sets scaling factor of the color bitmap font.
</description>
</method>
<method name="_font_set_script_support_override" qualifiers="virtual">
@@ -774,6 +987,8 @@
<param index="1" name="script" type="String" />
<param index="2" name="supported" type="bool" />
<description>
+ [b]Optional.[/b]
+ Adds override for [method _font_is_script_supported].
</description>
</method>
<method name="_font_set_spacing" qualifiers="virtual">
@@ -782,6 +997,8 @@
<param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
<param index="2" name="value" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets the spacing for [param spacing] (see [enum TextServer.SpacingType]) to [param value] in pixels (not relative to the font size).
</description>
</method>
<method name="_font_set_stretch" qualifiers="virtual">
@@ -789,6 +1006,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="stretch" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets font stretch amount, compared to a normal width. A percentage value between [code]50%[/code] and [code]200%[/code].
</description>
</method>
<method name="_font_set_style" qualifiers="virtual">
@@ -796,6 +1015,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="style" type="int" enum="TextServer.FontStyle" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Sets the font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="_font_set_style_name" qualifiers="virtual">
@@ -803,6 +1024,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="name_style" type="String" />
<description>
+ [b]Optional.[/b]
+ Sets the font style name.
</description>
</method>
<method name="_font_set_subpixel_positioning" qualifiers="virtual">
@@ -810,6 +1033,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="subpixel_positioning" type="int" enum="TextServer.SubpixelPositioning" />
<description>
+ [b]Optional.[/b]
+ Sets font subpixel glyph positioning mode.
</description>
</method>
<method name="_font_set_texture_image" qualifiers="virtual">
@@ -819,6 +1044,8 @@
<param index="2" name="texture_index" type="int" />
<param index="3" name="image" type="Image" />
<description>
+ [b]Required.[/b]
+ Sets font cache texture image data.
</description>
</method>
<method name="_font_set_texture_offsets" qualifiers="virtual">
@@ -828,6 +1055,8 @@
<param index="2" name="texture_index" type="int" />
<param index="3" name="offset" type="PackedInt32Array" />
<description>
+ [b]Optional.[/b]
+ Sets array containing glyph packing data.
</description>
</method>
<method name="_font_set_transform" qualifiers="virtual">
@@ -835,6 +1064,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="transform" type="Transform2D" />
<description>
+ [b]Optional.[/b]
+ Sets 2D transform, applied to the font outlines, can be used for slanting, flipping, and rotating glyphs.
</description>
</method>
<method name="_font_set_underline_position" qualifiers="virtual">
@@ -843,6 +1074,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="underline_position" type="float" />
<description>
+ [b]Required.[/b]
+ Sets pixel offset of the underline below the baseline.
</description>
</method>
<method name="_font_set_underline_thickness" qualifiers="virtual">
@@ -851,6 +1084,8 @@
<param index="1" name="size" type="int" />
<param index="2" name="underline_thickness" type="float" />
<description>
+ [b]Required.[/b]
+ Sets thickness of the underline in pixels.
</description>
</method>
<method name="_font_set_variation_coordinates" qualifiers="virtual">
@@ -858,6 +1093,8 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="variation_coordinates" type="Dictionary" />
<description>
+ [b]Optional.[/b]
+ Sets variation coordinates for the specified font cache entry.
</description>
</method>
<method name="_font_set_weight" qualifiers="virtual">
@@ -865,36 +1102,48 @@
<param index="0" name="font_rid" type="RID" />
<param index="1" name="weight" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets weight (boldness) of the font. A value in the [code]100...999[/code] range, normal font weight is [code]400[/code], bold font weight is [code]700[/code].
</description>
</method>
<method name="_font_supported_feature_list" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns the dictionary of the supported OpenType features.
</description>
</method>
<method name="_font_supported_variation_list" qualifiers="virtual const">
<return type="Dictionary" />
<param index="0" name="font_rid" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns the dictionary of the supported OpenType variation coordinates.
</description>
</method>
<method name="_format_number" qualifiers="virtual const">
<return type="String" />
- <param index="0" name="string" type="String" />
+ <param index="0" name="number" type="String" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Converts a number from the Western Arabic (0..9) to the numeral systems used in [param language].
</description>
</method>
<method name="_free_rid" qualifiers="virtual">
<return type="void" />
<param index="0" name="rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Frees an object created by this [TextServer].
</description>
</method>
<method name="_get_features" qualifiers="virtual const">
<return type="int" />
<description>
+ [b]Required.[/b]
+ Returns text server features, see [enum TextServer.Feature].
</description>
</method>
<method name="_get_hex_code_box_size" qualifiers="virtual const">
@@ -902,33 +1151,45 @@
<param index="0" name="size" type="int" />
<param index="1" name="index" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns size of the replacement character (box with character hexadecimal code that is drawn in place of invalid characters).
</description>
</method>
<method name="_get_name" qualifiers="virtual const">
<return type="String" />
<description>
+ [b]Required.[/b]
+ Returns the name of the server interface.
</description>
</method>
<method name="_get_support_data_filename" qualifiers="virtual const">
<return type="String" />
<description>
+ [b]Optional.[/b]
+ Returns default TextServer database (e.g. ICU break iterators and dictionaries) filename.
</description>
</method>
<method name="_get_support_data_info" qualifiers="virtual const">
<return type="String" />
<description>
+ [b]Optional.[/b]
+ Returns TextServer database (e.g. ICU break iterators and dictionaries) description.
</description>
</method>
<method name="_has" qualifiers="virtual">
<return type="bool" />
<param index="0" name="rid" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if [param rid] is valid resource owned by this text server.
</description>
</method>
<method name="_has_feature" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="feature" type="int" enum="TextServer.Feature" />
<description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if the server supports a feature.
</description>
</method>
<method name="_is_confusable" qualifiers="virtual const">
@@ -936,37 +1197,49 @@
<param index="0" name="string" type="String" />
<param index="1" name="dict" type="PackedStringArray" />
<description>
+ [b]Optional.[/b]
+ Returns index of the first string in [param dict] which is visually confusable with the [param string], or [code]-1[/code] if none is found.
</description>
</method>
<method name="_is_locale_right_to_left" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="locale" type="String" />
<description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if locale is right-to-left.
</description>
</method>
<method name="_is_valid_identifier" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="string" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if [param string] is a valid identifier.
</description>
</method>
<method name="_load_support_data" qualifiers="virtual">
<return type="bool" />
<param index="0" name="filename" type="String" />
<description>
+ [b]Optional.[/b]
+ Loads optional TextServer database (e.g. ICU break iterators and dictionaries).
</description>
</method>
<method name="_name_to_tag" qualifiers="virtual const">
<return type="int" />
<param index="0" name="name" type="String" />
<description>
+ [b]Optional.[/b]
+ Converts readable feature, variation, script, or language name to OpenType tag.
</description>
</method>
<method name="_parse_number" qualifiers="virtual const">
<return type="String" />
- <param index="0" name="string" type="String" />
+ <param index="0" name="number" type="String" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Converts [param number] from the numeral systems used in [param language] to Western Arabic (0..9).
</description>
</method>
<method name="_parse_structured_text" qualifiers="virtual const">
@@ -975,24 +1248,32 @@
<param index="1" name="args" type="Array" />
<param index="2" name="text" type="String" />
<description>
+ [b]Optional.[/b]
+ Default implementation of the BiDi algorithm override function. See [enum TextServer.StructuredTextParser] for more info.
</description>
</method>
<method name="_percent_sign" qualifiers="virtual const">
<return type="String" />
<param index="0" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns percent sign used in the [param language].
</description>
</method>
<method name="_save_support_data" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="filename" type="String" />
<description>
+ [b]Optional.[/b]
+ Saves optional TextServer database (e.g. ICU break iterators and dictionaries) to the file.
</description>
</method>
<method name="_shaped_get_span_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns number of text spans added using [method _shaped_text_add_string] or [method _shaped_text_add_object].
</description>
</method>
<method name="_shaped_get_span_meta" qualifiers="virtual const">
@@ -1000,6 +1281,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="index" type="int" />
<description>
+ [b]Required.[/b]
+ Returns text span metadata.
</description>
</method>
<method name="_shaped_set_span_update_font" qualifiers="virtual">
@@ -1010,6 +1293,8 @@
<param index="3" name="size" type="int" />
<param index="4" name="opentype_features" type="Dictionary" />
<description>
+ [b]Required.[/b]
+ Changes text span font, font size, and OpenType features, without changing the text.
</description>
</method>
<method name="_shaped_text_add_object" qualifiers="virtual">
@@ -1021,6 +1306,8 @@
<param index="4" name="length" type="int" />
<param index="5" name="baseline" type="float" />
<description>
+ [b]Required.[/b]
+ Adds inline object to the text buffer, [param key] must be unique. In the text, object is represented as [param length] object replacement characters.
</description>
</method>
<method name="_shaped_text_add_string" qualifiers="virtual">
@@ -1033,12 +1320,16 @@
<param index="5" name="language" type="String" />
<param index="6" name="meta" type="Variant" />
<description>
+ [b]Required.[/b]
+ Adds text span and font to draw it to the text buffer.
</description>
</method>
<method name="_shaped_text_clear" qualifiers="virtual">
<return type="void" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Clears text buffer (removes text and inline objects).
</description>
</method>
<method name="_shaped_text_closest_character_pos" qualifiers="virtual const">
@@ -1046,6 +1337,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns composite character position closest to the [param pos].
</description>
</method>
<method name="_shaped_text_draw" qualifiers="virtual const">
@@ -1057,6 +1350,8 @@
<param index="4" name="clip_r" type="float" />
<param index="5" name="color" type="Color" />
<description>
+ [b]Optional.[/b]
+ Draw shaped text into a canvas item at a given position, with [param color]. [param pos] specifies the leftmost point of the baseline (for horizontal layout) or topmost point of the baseline (for vertical layout).
</description>
</method>
<method name="_shaped_text_draw_outline" qualifiers="virtual const">
@@ -1069,6 +1364,8 @@
<param index="5" name="outline_size" type="int" />
<param index="6" name="color" type="Color" />
<description>
+ [b]Optional.[/b]
+ Draw the outline of the shaped text into a canvas item at a given position, with [param color]. [param pos] specifies the leftmost point of the baseline (for horizontal layout) or topmost point of the baseline (for vertical layout).
</description>
</method>
<method name="_shaped_text_fit_to_width" qualifiers="virtual">
@@ -1077,12 +1374,16 @@
<param index="1" name="width" type="float" />
<param index="2" name="justification_flags" type="int" enum="TextServer.JustificationFlag" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Adjusts text width to fit to specified width, returns new text width.
</description>
</method>
<method name="_shaped_text_get_ascent" qualifiers="virtual const">
<return type="float" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns the text ascent (number of pixels above the baseline for horizontal layout or to the left of baseline for vertical).
</description>
</method>
<method name="_shaped_text_get_carets" qualifiers="virtual const">
@@ -1091,36 +1392,48 @@
<param index="1" name="position" type="int" />
<param index="2" name="caret" type="CaretInfo*" />
<description>
+ [b]Optional.[/b]
+ Returns shapes of the carets corresponding to the character offset [param position] in the text. Returned caret shape is 1 pixel wide rectangle.
</description>
</method>
<method name="_shaped_text_get_character_breaks" qualifiers="virtual const">
<return type="PackedInt32Array" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns array of the composite character boundaries.
</description>
</method>
<method name="_shaped_text_get_custom_ellipsis" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns ellipsis character used for text clipping.
</description>
</method>
<method name="_shaped_text_get_custom_punctuation" qualifiers="virtual const">
<return type="String" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="_shaped_text_get_descent" qualifiers="virtual const">
<return type="float" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns the text descent (number of pixels below the baseline for horizontal layout or to the right of baseline for vertical).
</description>
</method>
<method name="_shaped_text_get_direction" qualifiers="virtual const">
<return type="int" enum="TextServer.Direction" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns direction of the text.
</description>
</method>
<method name="_shaped_text_get_dominant_direction_in_range" qualifiers="virtual const">
@@ -1129,36 +1442,48 @@
<param index="1" name="start" type="int" />
<param index="2" name="end" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns dominant direction of in the range of text.
</description>
</method>
<method name="_shaped_text_get_ellipsis_glyph_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns number of glyphs in the ellipsis.
</description>
</method>
<method name="_shaped_text_get_ellipsis_glyphs" qualifiers="virtual const">
<return type="const Glyph*" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns array of the glyphs in the ellipsis.
</description>
</method>
<method name="_shaped_text_get_ellipsis_pos" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns position of the ellipsis.
</description>
</method>
<method name="_shaped_text_get_glyph_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns number of glyphs in the buffer.
</description>
</method>
<method name="_shaped_text_get_glyphs" qualifiers="virtual const">
<return type="const Glyph*" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns an array of glyphs in the visual order.
</description>
</method>
<method name="_shaped_text_get_grapheme_bounds" qualifiers="virtual const">
@@ -1166,12 +1491,16 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns composite character's bounds as offsets from the start of the line.
</description>
</method>
<method name="_shaped_text_get_inferred_direction" qualifiers="virtual const">
<return type="int" enum="TextServer.Direction" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns direction of the text, inferred by the BiDi algorithm.
</description>
</method>
<method name="_shaped_text_get_line_breaks" qualifiers="virtual const">
@@ -1181,6 +1510,8 @@
<param index="2" name="start" type="int" />
<param index="3" name="break_flags" type="int" enum="TextServer.LineBreakFlag" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Breaks text to the lines and returns character ranges for each line.
</description>
</method>
<method name="_shaped_text_get_line_breaks_adv" qualifiers="virtual const">
@@ -1191,6 +1522,8 @@
<param index="3" name="once" type="bool" />
<param index="4" name="break_flags" type="int" enum="TextServer.LineBreakFlag" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Breaks text to the lines and columns. Returns character ranges for each segment.
</description>
</method>
<method name="_shaped_text_get_object_glyph" qualifiers="virtual const">
@@ -1198,6 +1531,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="key" type="Variant" />
<description>
+ [b]Required.[/b]
+ Returns the glyph index of the inline object.
</description>
</method>
<method name="_shaped_text_get_object_range" qualifiers="virtual const">
@@ -1205,6 +1540,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="key" type="Variant" />
<description>
+ [b]Required.[/b]
+ Returns the character range of the inline object.
</description>
</method>
<method name="_shaped_text_get_object_rect" qualifiers="virtual const">
@@ -1212,42 +1549,56 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="key" type="Variant" />
<description>
+ [b]Required.[/b]
+ Returns bounding rectangle of the inline object.
</description>
</method>
<method name="_shaped_text_get_objects" qualifiers="virtual const">
<return type="Array" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns array of inline objects.
</description>
</method>
<method name="_shaped_text_get_orientation" qualifiers="virtual const">
<return type="int" enum="TextServer.Orientation" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns text orientation.
</description>
</method>
<method name="_shaped_text_get_parent" qualifiers="virtual const">
<return type="RID" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns the parent buffer from which the substring originates.
</description>
</method>
<method name="_shaped_text_get_preserve_control" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if text buffer is configured to display control characters.
</description>
</method>
<method name="_shaped_text_get_preserve_invalid" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if text buffer is configured to display hexadecimal codes in place of invalid characters.
</description>
</method>
<method name="_shaped_text_get_range" qualifiers="virtual const">
<return type="Vector2i" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns substring buffer character range in the parent buffer.
</description>
</method>
<method name="_shaped_text_get_selection" qualifiers="virtual const">
@@ -1256,12 +1607,16 @@
<param index="1" name="start" type="int" />
<param index="2" name="end" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns selection rectangles for the specified character range.
</description>
</method>
<method name="_shaped_text_get_size" qualifiers="virtual const">
<return type="Vector2" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns size of the text.
</description>
</method>
<method name="_shaped_text_get_spacing" qualifiers="virtual const">
@@ -1269,30 +1624,40 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
<description>
+ [b]Optional.[/b]
+ Returns extra spacing added between glyphs or lines in pixels.
</description>
</method>
<method name="_shaped_text_get_trim_pos" qualifiers="virtual const">
<return type="int" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns the position of the overrun trim.
</description>
</method>
<method name="_shaped_text_get_underline_position" qualifiers="virtual const">
<return type="float" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns pixel offset of the underline below the baseline.
</description>
</method>
<method name="_shaped_text_get_underline_thickness" qualifiers="virtual const">
<return type="float" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns thickness of the underline.
</description>
</method>
<method name="_shaped_text_get_width" qualifiers="virtual const">
<return type="float" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns width (for horizontal layout) or height (for vertical) of the text.
</description>
</method>
<method name="_shaped_text_get_word_breaks" qualifiers="virtual const">
@@ -1300,6 +1665,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="grapheme_flags" type="int" enum="TextServer.GraphemeFlag" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Breaks text into words and returns array of character ranges. Use [param grapheme_flags] to set what characters are used for breaking (see [enum TextServer.GraphemeFlag]).
</description>
</method>
<method name="_shaped_text_hit_test_grapheme" qualifiers="virtual const">
@@ -1307,6 +1674,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="coord" type="float" />
<description>
+ [b]Optional.[/b]
+ Returns grapheme index at the specified pixel offset at the baseline, or [code]-1[/code] if none is found.
</description>
</method>
<method name="_shaped_text_hit_test_position" qualifiers="virtual const">
@@ -1314,12 +1683,16 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="coord" type="float" />
<description>
+ [b]Optional.[/b]
+ Returns caret character offset at the specified pixel offset at the baseline. This function always returns a valid position.
</description>
</method>
<method name="_shaped_text_is_ready" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns [code]true[/code] if buffer is successfully shaped.
</description>
</method>
<method name="_shaped_text_next_character_pos" qualifiers="virtual const">
@@ -1327,6 +1700,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns composite character end position closest to the [param pos].
</description>
</method>
<method name="_shaped_text_next_grapheme_pos" qualifiers="virtual const">
@@ -1334,6 +1709,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns grapheme end position closest to the [param pos].
</description>
</method>
<method name="_shaped_text_overrun_trim_to_width" qualifiers="virtual">
@@ -1342,6 +1719,8 @@
<param index="1" name="width" type="float" />
<param index="2" name="trim_flags" type="int" enum="TextServer.TextOverrunFlag" is_bitfield="true" />
<description>
+ [b]Optional.[/b]
+ Trims text if it exceeds the given width.
</description>
</method>
<method name="_shaped_text_prev_character_pos" qualifiers="virtual const">
@@ -1349,6 +1728,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns composite character start position closest to the [param pos].
</description>
</method>
<method name="_shaped_text_prev_grapheme_pos" qualifiers="virtual const">
@@ -1356,6 +1737,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="pos" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns grapheme start position closest to the [param pos].
</description>
</method>
<method name="_shaped_text_resize_object" qualifiers="virtual">
@@ -1366,6 +1749,8 @@
<param index="3" name="inline_align" type="int" enum="InlineAlignment" />
<param index="4" name="baseline" type="float" />
<description>
+ [b]Required.[/b]
+ Sets new size and alignment of embedded object.
</description>
</method>
<method name="_shaped_text_set_bidi_override" qualifiers="virtual">
@@ -1373,6 +1758,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="override" type="Array" />
<description>
+ [b]Optional.[/b]
+ Overrides BiDi for the structured text.
</description>
</method>
<method name="_shaped_text_set_custom_ellipsis" qualifiers="virtual">
@@ -1380,6 +1767,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="char" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets ellipsis character used for text clipping.
</description>
</method>
<method name="_shaped_text_set_custom_punctuation" qualifiers="virtual">
@@ -1387,6 +1776,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="punct" type="String" />
<description>
+ [b]Optional.[/b]
+ Sets custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
</description>
</method>
<method name="_shaped_text_set_direction" qualifiers="virtual">
@@ -1394,6 +1785,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="direction" type="int" enum="TextServer.Direction" />
<description>
+ [b]Optional.[/b]
+ Sets desired text direction. If set to [constant TextServer.DIRECTION_AUTO], direction will be detected based on the buffer contents and current locale.
</description>
</method>
<method name="_shaped_text_set_orientation" qualifiers="virtual">
@@ -1401,6 +1794,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="orientation" type="int" enum="TextServer.Orientation" />
<description>
+ [b]Optional.[/b]
+ Sets desired text orientation.
</description>
</method>
<method name="_shaped_text_set_preserve_control" qualifiers="virtual">
@@ -1408,6 +1803,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code] text buffer will display control characters.
</description>
</method>
<method name="_shaped_text_set_preserve_invalid" qualifiers="virtual">
@@ -1415,6 +1812,8 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
+ [b]Optional.[/b]
+ If set to [code]true[/code] text buffer will display invalid characters as hexadecimal codes, otherwise nothing is displayed.
</description>
</method>
<method name="_shaped_text_set_spacing" qualifiers="virtual">
@@ -1423,18 +1822,24 @@
<param index="1" name="spacing" type="int" enum="TextServer.SpacingType" />
<param index="2" name="value" type="int" />
<description>
+ [b]Optional.[/b]
+ Sets extra spacing added between glyphs or lines in pixels.
</description>
</method>
<method name="_shaped_text_shape" qualifiers="virtual">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Shapes buffer if it's not shaped. Returns [code]true[/code] if the string is shaped successfully.
</description>
</method>
<method name="_shaped_text_sort_logical" qualifiers="virtual">
<return type="const Glyph*" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Required.[/b]
+ Returns text glyphs in the logical order.
</description>
</method>
<method name="_shaped_text_substr" qualifiers="virtual const">
@@ -1443,6 +1848,8 @@
<param index="1" name="start" type="int" />
<param index="2" name="length" type="int" />
<description>
+ [b]Required.[/b]
+ Returns text buffer for the substring of the text in the [param shaped] text buffer (including inline objects).
</description>
</method>
<method name="_shaped_text_tab_align" qualifiers="virtual">
@@ -1450,24 +1857,32 @@
<param index="0" name="shaped" type="RID" />
<param index="1" name="tab_stops" type="PackedFloat32Array" />
<description>
+ [b]Optional.[/b]
+ Aligns shaped text to the given tab-stops.
</description>
</method>
<method name="_shaped_text_update_breaks" qualifiers="virtual">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Updates break points in the shaped text. This method is called by default implementation of text breaking functions.
</description>
</method>
<method name="_shaped_text_update_justification_ops" qualifiers="virtual">
<return type="bool" />
<param index="0" name="shaped" type="RID" />
<description>
+ [b]Optional.[/b]
+ Updates justification points in the shaped text. This method is called by default implementation of text justification functions.
</description>
</method>
<method name="_spoof_check" qualifiers="virtual const">
<return type="bool" />
<param index="0" name="string" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns [code]true[/code] if [param string] is likely to be an attempt at confusing the reader.
</description>
</method>
<method name="_string_get_character_breaks" qualifiers="virtual const">
@@ -1475,6 +1890,8 @@
<param index="0" name="string" type="String" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns array of the composite character boundaries.
</description>
</method>
<method name="_string_get_word_breaks" qualifiers="virtual const">
@@ -1483,6 +1900,8 @@
<param index="1" name="language" type="String" />
<param index="2" name="chars_per_line" type="int" />
<description>
+ [b]Optional.[/b]
+ Returns an array of the word break boundaries. Elements in the returned array are the offsets of the start and end of words. Therefore the length of the array is always even.
</description>
</method>
<method name="_string_to_lower" qualifiers="virtual const">
@@ -1490,6 +1909,8 @@
<param index="0" name="string" type="String" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns the string converted to lowercase.
</description>
</method>
<method name="_string_to_upper" qualifiers="virtual const">
@@ -1497,18 +1918,24 @@
<param index="0" name="string" type="String" />
<param index="1" name="language" type="String" />
<description>
+ [b]Optional.[/b]
+ Returns the string converted to uppercase.
</description>
</method>
<method name="_strip_diacritics" qualifiers="virtual const">
<return type="String" />
<param index="0" name="string" type="String" />
<description>
+ [b]Optional.[/b]
+ Strips diacritics from the string.
</description>
</method>
<method name="_tag_to_name" qualifiers="virtual const">
<return type="String" />
<param index="0" name="tag" type="int" />
<description>
+ [b]Optional.[/b]
+ Converts OpenType tag to readable feature, variation, script, or language name.
</description>
</method>
</methods>
diff --git a/drivers/d3d12/rendering_context_driver_d3d12.h b/drivers/d3d12/rendering_context_driver_d3d12.h
index f74105ed3d..2e286b6927 100644
--- a/drivers/d3d12/rendering_context_driver_d3d12.h
+++ b/drivers/d3d12/rendering_context_driver_d3d12.h
@@ -100,7 +100,7 @@ public:
// D3D12-only methods.
struct Surface {
- HWND hwnd = NULL;
+ HWND hwnd = nullptr;
uint32_t width = 0;
uint32_t height = 0;
DisplayServer::VSyncMode vsync_mode = DisplayServer::VSYNC_ENABLED;
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 49ab4f179f..fc7aa62aae 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -5999,7 +5999,7 @@ RenderingDeviceDriverD3D12::~RenderingDeviceDriverD3D12() {
}
bool RenderingDeviceDriverD3D12::is_in_developer_mode() {
- HKEY hkey = NULL;
+ HKEY hkey = nullptr;
LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AppModelUnlock", 0, KEY_READ, &hkey);
if (result != ERROR_SUCCESS) {
return false;
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h
index 852cb9db0e..61da9a5169 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.h
+++ b/drivers/d3d12/rendering_device_driver_d3d12.h
@@ -379,7 +379,7 @@ private:
struct FenceInfo {
ComPtr<ID3D12Fence> d3d_fence = nullptr;
- HANDLE event_handle = NULL;
+ HANDLE event_handle = nullptr;
UINT64 fence_value = 0;
};
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index de990a4222..304dc9e328 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -1287,7 +1287,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index, Ren
uint32_t range_start = state.canvas_instance_batches[p_index].start * sizeof(InstanceData);
_enable_attributes(range_start, false);
- glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, state.canvas_instance_batches[p_index].instance_count);
+ glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, state.canvas_instance_batches[p_index].instance_count);
glBindVertexArray(0);
if (r_render_info) {
@@ -1490,7 +1490,7 @@ void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index, Ren
uint32_t vertex_count = mesh_storage->mesh_surface_get_vertices_drawn_count(surface);
if (use_index_buffer) {
- glDrawElementsInstanced(primitive_gl, vertex_count, mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count);
+ glDrawElementsInstanced(primitive_gl, vertex_count, mesh_storage->mesh_surface_get_index_type(surface), nullptr, instance_count);
} else {
glDrawArraysInstanced(primitive_gl, 0, vertex_count, instance_count);
}
@@ -1701,7 +1701,7 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c
}
glBindVertexArray(co->vertex_array);
- glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0);
+ glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, nullptr);
instance = instance->next;
}
@@ -1804,7 +1804,7 @@ void RasterizerCanvasGLES3::light_update_directional_shadow(RID p_rid, int p_sha
}
glBindVertexArray(co->vertex_array);
- glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, 0);
+ glDrawElements(GL_TRIANGLES, 3 * co->line_point_count, GL_UNSIGNED_SHORT, nullptr);
instance = instance->next;
}
@@ -1923,7 +1923,7 @@ void RasterizerCanvasGLES3::render_sdf(RID p_render_target, LightOccluderInstanc
shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::MODELVIEW2, modelview.columns[0][1], modelview.columns[1][1], 0, modelview.columns[2][1], shadow_render.shader_version, variant);
glBindVertexArray(oc->sdf_vertex_array);
- glDrawElements(oc->sdf_is_lines ? GL_LINES : GL_TRIANGLES, oc->sdf_index_count, GL_UNSIGNED_INT, 0);
+ glDrawElements(oc->sdf_is_lines ? GL_LINES : GL_TRIANGLES, oc->sdf_index_count, GL_UNSIGNED_INT, nullptr);
instance = instance->next;
}
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index 73ee277074..cee4f93b3d 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -307,7 +307,7 @@ RasterizerGLES3::RasterizerGLES3() {
if (callback) {
print_line("godot: ENABLING GL DEBUG");
glEnable(_EXT_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
- callback((DEBUGPROCARB)_gl_debug_print, NULL);
+ callback((DEBUGPROCARB)_gl_debug_print, nullptr);
glEnable(_EXT_DEBUG_OUTPUT);
}
}
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 09edc12112..29cfa251d6 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -3462,10 +3462,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
}
if (use_wireframe) {
- glDrawElementsInstanced(GL_LINES, count, GL_UNSIGNED_INT, 0, inst->instance_count);
+ glDrawElementsInstanced(GL_LINES, count, GL_UNSIGNED_INT, nullptr, inst->instance_count);
} else {
if (use_index_buffer) {
- glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0, inst->instance_count);
+ glDrawElementsInstanced(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), nullptr, inst->instance_count);
} else {
glDrawArraysInstanced(primitive_gl, 0, count, inst->instance_count);
}
@@ -3473,10 +3473,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
} else {
// Using regular Mesh.
if (use_wireframe) {
- glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, 0);
+ glDrawElements(GL_LINES, count, GL_UNSIGNED_INT, nullptr);
} else {
if (use_index_buffer) {
- glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), 0);
+ glDrawElements(primitive_gl, count, mesh_storage->mesh_surface_get_index_type(mesh_surface), nullptr);
} else {
glDrawArrays(primitive_gl, 0, count);
}
diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp
index 43b146152d..876309d22c 100644
--- a/drivers/gles3/shader_gles3.cpp
+++ b/drivers/gles3/shader_gles3.cpp
@@ -540,7 +540,7 @@ bool ShaderGLES3::_load_from_cache(Version *p_version) {
return false;
#else
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
- if (RasterizerGLES3::is_gles_over_gl() && (glProgramBinary == NULL)) { // ARB_get_program_binary extension not available.
+ if (RasterizerGLES3::is_gles_over_gl() && (glProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
return false;
}
#endif
@@ -627,7 +627,7 @@ void ShaderGLES3::_save_to_cache(Version *p_version) {
#else
ERR_FAIL_COND(!shader_cache_dir_valid);
#if !defined(ANDROID_ENABLED) && !defined(IOS_ENABLED)
- if (RasterizerGLES3::is_gles_over_gl() && (glGetProgramBinary == NULL)) { // ARB_get_program_binary extension not available.
+ if (RasterizerGLES3::is_gles_over_gl() && (glGetProgramBinary == nullptr)) { // ARB_get_program_binary extension not available.
return;
}
#endif
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 5d2bc44377..a032535827 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1067,7 +1067,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
glBindTexture(GL_TEXTURE_2D, temp_color_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -1139,7 +1139,7 @@ Ref<Image> TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) cons
glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
glBindTexture(GL_TEXTURE_2D, temp_color_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
@@ -1248,7 +1248,7 @@ Vector<Ref<Image>> TextureStorage::texture_3d_get(RID p_texture) const {
glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
glBindTexture(GL_TEXTURE_2D, temp_color_texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 83e71292a3..ff42b82435 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -439,7 +439,7 @@ bool EditorFileSystem::_test_for_reimport(const String &p_path, bool p_only_impo
}
}
- if (importer_name == "keep") {
+ if (importer_name == "keep" || importer_name == "skip") {
return false; //keep mode, do not reimport
}
@@ -1859,7 +1859,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
source_file_options[p_files[i]] = HashMap<StringName, Variant>();
importer_name = file_importer_name;
- if (importer_name == "keep") {
+ if (importer_name == "keep" || importer_name == "skip") {
continue; //do nothing
}
@@ -1885,7 +1885,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
base_paths[p_files[i]] = ResourceFormatImporter::get_singleton()->get_import_base_path(p_files[i]);
}
- if (importer_name == "keep") {
+ if (importer_name == "keep" || importer_name == "skip") {
return OK; // (do nothing)
}
@@ -2078,7 +2078,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
}
}
- if (importer_name == "keep") {
+ if (importer_name == "keep" || importer_name == "skip") {
//keep files, do nothing.
fs->files[cpos]->modified_time = FileAccess::get_modified_time(p_file);
fs->files[cpos]->import_modified_time = FileAccess::get_modified_time(p_file + ".import");
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index c212bdb84f..d2a187bcfd 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -1196,6 +1196,11 @@ Error EditorExportPlatform::export_project_files(bool p_main_pack, const Ref<Edi
String importer_type = config->get_value("remap", "importer");
+ if (importer_type == "skip") {
+ // Skip file.
+ continue;
+ }
+
if (importer_type == "keep") {
// Just keep file as-is.
Vector<uint8_t> array = FileAccess::get_file_as_bytes(path);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 88fed16db1..fc170d606a 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1160,7 +1160,7 @@ void FileSystemDock::_select_file(const String &p_path, bool p_select_in_favorit
if (err == OK) {
if (config->has_section_key("remap", "importer")) {
String importer = config->get_value("remap", "importer");
- if (importer == "keep") {
+ if (importer == "keep" || importer == "skip") {
EditorNode::get_singleton()->show_warning(TTR("Importing has been disabled for this file, so it can't be opened for editing."));
return;
}
diff --git a/editor/icons/FadeCross.svg b/editor/icons/FadeCross.svg
new file mode 100644
index 0000000000..2d4f058838
--- /dev/null
+++ b/editor/icons/FadeCross.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 1v2h2.586l-3.586 3.586-4.293-4.293-1.414 1.414 4.293 4.293-4.293 4.293 1.414 1.414 4.293-4.293 3.586 3.586h-2.586v2h5a1 1 0 0 0 1-1v-5h-2v2.586l-3.586-3.586 3.586-3.586v2.586h2v-5a1 1 0 0 0 -1-1z" fill="#e0e0e0"/></svg> \ No newline at end of file
diff --git a/editor/icons/FadeDisabled.svg b/editor/icons/FadeDisabled.svg
new file mode 100644
index 0000000000..2333335dcd
--- /dev/null
+++ b/editor/icons/FadeDisabled.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m11.032091 3.1108593-1.4142134 1.4142136 1.8285784 1.8285781h-9.8600786v1.999698h9.8600786l-1.8285784 1.828578 1.4142134 1.414214 3.535534-3.5355342c.390524-.3905243.390525-1.0236891 0-1.4142136z" fill="#e0e0e0"/></svg> \ No newline at end of file
diff --git a/editor/icons/FadeIn.svg b/editor/icons/FadeIn.svg
new file mode 100644
index 0000000000..3144e07d23
--- /dev/null
+++ b/editor/icons/FadeIn.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m9 1v2h2.586c-9.293 9.293-.896067.8960673-9.293 9.293l1.414 1.414c9.293-9.293 3.7072139-3.7072139 9.293-9.293v2.586h2v-5c0-.5522847-.447715-1-1-1z" fill="#e0e0e0"/></svg> \ No newline at end of file
diff --git a/editor/icons/FadeOut.svg b/editor/icons/FadeOut.svg
new file mode 100644
index 0000000000..4ce90b58aa
--- /dev/null
+++ b/editor/icons/FadeOut.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.707 2.293-1.414 1.414c9.293 9.293 1.4503342 1.4503342 9.293 9.293h-2.586v2h5c.552285 0 1-.447715 1-1v-5h-2v2.586c-9.293-9.293-2.832166-2.8321664-9.293-9.293z" fill="#e0e0e0"/></svg> \ No newline at end of file
diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp
index a2cb306c94..085ca1362d 100644
--- a/editor/import/resource_importer_bmfont.cpp
+++ b/editor/import/resource_importer_bmfont.cpp
@@ -30,6 +30,7 @@
#include "resource_importer_bmfont.h"
+#include "core/io/config_file.h"
#include "core/io/resource_saver.h"
String ResourceImporterBMFont::get_importer_name() const {
@@ -75,9 +76,24 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
Ref<FontFile> font;
font.instantiate();
- Error err = font->load_bitmap_font(p_source_file);
+ List<String> image_files;
+ Error err = font->_load_bitmap_font(p_source_file, &image_files);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load font to file \"" + p_source_file + "\".");
+ // Update import settings for the image files used by font.
+ for (List<String>::Element *E = image_files.front(); E; E = E->next()) {
+ Ref<ConfigFile> config;
+ config.instantiate();
+
+ err = config->load(E->get() + ".import");
+ if (err == OK) {
+ config->clear();
+ config->set_value("remap", "importer", "skip");
+
+ config->save(E->get() + ".import");
+ }
+ }
+
font->set_allow_system_fallback(false);
font->set_fallbacks(fallbacks);
font->set_fixed_size_scale_mode(smode);
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index 47572a991c..6f4a376496 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -48,7 +48,8 @@ public:
Ref<ResourceImporter> importer;
Vector<String> paths;
HashSet<StringName> checked;
- bool checking;
+ bool checking = false;
+ bool skip = false;
String base_options_path;
bool _set(const StringName &p_name, const Variant &p_value) {
@@ -91,10 +92,6 @@ public:
void update() {
notify_property_list_changed();
}
-
- ImportDockParameters() {
- checking = false;
- }
};
ImportDock *ImportDock::singleton = nullptr;
@@ -109,8 +106,16 @@ void ImportDock::set_edit_path(const String &p_path) {
}
String importer_name = config->get_value("remap", "importer");
-
- params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
+ if (importer_name == "keep") {
+ params->importer.unref();
+ params->skip = false;
+ } else if (importer_name == "skip") {
+ params->importer.unref();
+ params->skip = true;
+ } else {
+ params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
+ params->skip = false;
+ }
params->paths.clear();
params->paths.push_back(p_path);
@@ -152,9 +157,13 @@ void ImportDock::set_edit_path(const String &p_path) {
void ImportDock::_add_keep_import_option(const String &p_importer_name) {
import_as->add_separator();
- import_as->add_item(TTR("Keep File (No Import)"));
+ import_as->add_item(TTR("Keep File (exported as is)"));
import_as->set_item_metadata(-1, "keep");
+ import_as->add_item(TTR("Skip File (not exported)"));
+ import_as->set_item_metadata(-1, "skip");
if (p_importer_name == "keep") {
+ import_as->select(import_as->get_item_count() - 2);
+ } else if (p_importer_name == "skip") {
import_as->select(import_as->get_item_count() - 1);
}
}
@@ -163,7 +172,7 @@ void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_
// Set the importer class to fetch the correct class in the XML class reference.
// This allows tooltips to display when hovering properties.
if (params->importer != nullptr) {
- // Null check to avoid crashing if the "Keep File (No Import)" mode is selected.
+ // Null check to avoid crashing if the "Keep File (exported as is)" mode is selected.
import_opts->set_object_class(params->importer->get_class_name());
}
@@ -215,7 +224,17 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
ERR_CONTINUE(err != OK);
if (i == 0) {
- params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(config->get_value("remap", "importer"));
+ String importer_name = config->get_value("remap", "importer");
+ if (importer_name == "keep") {
+ params->importer.unref();
+ params->skip = false;
+ } else if (importer_name == "skip") {
+ params->importer.unref();
+ params->skip = true;
+ } else {
+ params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
+ params->skip = false;
+ }
if (params->importer.is_null()) {
clear();
return;
@@ -372,12 +391,18 @@ void ImportDock::_importer_selected(int i_idx) {
String name = import_as->get_selected_metadata();
if (name == "keep") {
params->importer.unref();
+ params->skip = false;
+ _update_options(params->base_options_path, Ref<ConfigFile>());
+ } else if (name == "skip") {
+ params->importer.unref();
+ params->skip = true;
_update_options(params->base_options_path, Ref<ConfigFile>());
} else {
Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(name);
ERR_FAIL_COND(importer.is_null());
params->importer = importer;
+ params->skip = false;
Ref<ConfigFile> config;
if (params->paths.size()) {
String path = params->paths[0];
@@ -490,7 +515,11 @@ void ImportDock::_reimport_attempt() {
if (params->importer.is_valid()) {
importer_name = params->importer->get_importer_name();
} else {
- importer_name = "keep";
+ if (params->skip) {
+ importer_name = "skip";
+ } else {
+ importer_name = "keep";
+ }
}
for (int i = 0; i < params->paths.size(); i++) {
Ref<ConfigFile> config;
@@ -566,6 +595,7 @@ void ImportDock::_advanced_options() {
params->importer->show_advanced_options(params->paths[0]);
}
}
+
void ImportDock::_reimport() {
for (int i = 0; i < params->paths.size(); i++) {
Ref<ConfigFile> config;
@@ -611,7 +641,11 @@ void ImportDock::_reimport() {
} else {
//set to no import
config->clear();
- config->set_value("remap", "importer", "keep");
+ if (params->skip) {
+ config->set_value("remap", "importer", "skip");
+ } else {
+ config->set_value("remap", "importer", "keep");
+ }
}
config->save(params->paths[i] + ".import");
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 5845ddbb6e..597fd7393f 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -946,8 +946,10 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_center_view = memnew(Button);
button_center_view->set_anchors_and_offsets_preset(Control::PRESET_TOP_RIGHT, Control::PRESET_MODE_MINSIZE, 5);
+ button_center_view->set_grow_direction_preset(Control::PRESET_TOP_RIGHT);
button_center_view->connect("pressed", callable_mp(this, &GenericTilePolygonEditor::_center_view));
button_center_view->set_theme_type_variation("FlatButton");
+ button_center_view->set_tooltip_text(TTR("Center View"));
button_center_view->set_disabled(true);
root->add_child(button_center_view);
diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index a54ffd4ee8..9460f9fef2 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -464,7 +464,7 @@ void VersionControlEditorPlugin::_force_push() {
void VersionControlEditorPlugin::_update_opened_tabs() {
Vector<EditorData::EditedScene> open_scenes = EditorNode::get_editor_data().get_edited_scenes();
for (int i = 0; i < open_scenes.size(); i++) {
- if (open_scenes[i].root == NULL) {
+ if (open_scenes[i].root == nullptr) {
continue;
}
EditorNode::get_singleton()->reload_scene(open_scenes[i].path);
diff --git a/main/main.cpp b/main/main.cpp
index 15f55fe180..9215f2e848 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -74,14 +74,15 @@
#include "servers/navigation_server_3d.h"
#include "servers/navigation_server_3d_dummy.h"
#include "servers/physics_server_2d.h"
-#ifndef _3D_DISABLED
-#include "servers/physics_server_3d.h"
-#endif // _3D_DISABLED
#include "servers/register_server_types.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/text/text_server_dummy.h"
#include "servers/text_server.h"
+
+#ifndef _3D_DISABLED
+#include "servers/physics_server_3d.h"
#include "servers/xr_server.h"
+#endif // _3D_DISABLED
#ifdef TESTS_ENABLED
#include "tests/test_main.h"
@@ -144,17 +145,17 @@ static AudioServer *audio_server = nullptr;
static DisplayServer *display_server = nullptr;
static RenderingServer *rendering_server = nullptr;
static CameraServer *camera_server = nullptr;
-static XRServer *xr_server = nullptr;
static TextServerManager *tsman = nullptr;
-#ifndef _3D_DISABLED
-static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
-static PhysicsServer3D *physics_server_3d = nullptr;
-#endif // _3D_DISABLED
static PhysicsServer2DManager *physics_server_2d_manager = nullptr;
static PhysicsServer2D *physics_server_2d = nullptr;
static NavigationServer3D *navigation_server_3d = nullptr;
static NavigationServer2D *navigation_server_2d = nullptr;
static ThemeDB *theme_db = nullptr;
+#ifndef _3D_DISABLED
+static PhysicsServer3DManager *physics_server_3d_manager = nullptr;
+static PhysicsServer3D *physics_server_3d = nullptr;
+static XRServer *xr_server = nullptr;
+#endif // _3D_DISABLED
// We error out if setup2() doesn't turn this true
static bool _start_success = false;
@@ -1646,6 +1647,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->disable_crash_handler();
} else if (I->get() == "--skip-breakpoints") {
skip_breakpoints = true;
+#ifndef _3D_DISABLED
} else if (I->get() == "--xr-mode") {
if (I->next()) {
String xr_mode = I->next()->get().to_lower();
@@ -1664,7 +1666,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing --xr-mode argument, aborting.\n");
goto error;
}
-
+#endif // _3D_DISABLED
} else if (I->get() == "--benchmark") {
OS::get_singleton()->set_use_benchmark(true);
} else if (I->get() == "--benchmark-file") {
@@ -2788,6 +2790,7 @@ Error Main::setup2() {
OS::get_singleton()->benchmark_end_measure("Servers", "Audio");
}
+#ifndef _3D_DISABLED
/* Initialize XR Server */
{
@@ -2797,6 +2800,7 @@ Error Main::setup2() {
OS::get_singleton()->benchmark_end_measure("Servers", "XR");
}
+#endif // _3D_DISABLED
OS::get_singleton()->benchmark_end_measure("Startup", "Servers");
@@ -3942,7 +3946,9 @@ bool Main::iteration() {
bool exit = false;
// process all our active interfaces
+#ifndef _3D_DISABLED
XRServer::get_singleton()->_process();
+#endif // _3D_DISABLED
NavigationServer2D::get_singleton()->sync();
NavigationServer3D::get_singleton()->sync();
@@ -4161,11 +4167,13 @@ void Main::cleanup(bool p_force) {
//clear global shader variables before scene and other graphics stuff are deinitialized.
rendering_server->global_shader_parameters_clear();
+#ifndef _3D_DISABLED
if (xr_server) {
// Now that we're unregistering properly in plugins we need to keep access to xr_server for a little longer
// We do however unset our primary interface
xr_server->set_primary_interface(Ref<XRInterface>());
}
+#endif // _3D_DISABLED
#ifdef TOOLS_ENABLED
GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_EDITOR);
@@ -4195,9 +4203,11 @@ void Main::cleanup(bool p_force) {
EngineDebugger::deinitialize();
+#ifndef _3D_DISABLED
if (xr_server) {
memdelete(xr_server);
}
+#endif // _3D_DISABLED
if (audio_server) {
audio_server->finish();
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 3ec5098c21..6af6460b31 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -562,6 +562,12 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : p_class->annotations) {
+ resolve_annotation(E);
+ E->apply(parser, p_class, p_class->outer);
+ }
+
parser->current_class = previous_class;
return OK;
@@ -912,7 +918,6 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
{
#ifdef DEBUG_ENABLED
- HashSet<GDScriptWarning::Code> previously_ignored_warnings = parser->ignored_warnings;
GDScriptParser::Node *member_node = member.get_source_node();
if (member_node && member_node->type != GDScriptParser::Node::ANNOTATION) {
// Apply @warning_ignore annotations before resolving member.
@@ -922,9 +927,6 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
E->apply(parser, member.variable, p_class);
}
}
- for (GDScriptWarning::Code ignored_warning : member_node->ignored_warnings) {
- parser->ignored_warnings.insert(ignored_warning);
- }
}
#endif
switch (member.type) {
@@ -1061,6 +1063,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
enum_type.enum_values[element.identifier->name] = element.value;
dictionary[String(element.identifier->name)] = element.value;
+
+#ifdef DEBUG_ENABLED
+ // Named enum identifiers do not shadow anything since you can only access them with `NamedEnum.ENUM_VALUE`.
+ if (member.m_enum->identifier->name == StringName()) {
+ is_shadowing(element.identifier, "enum member", false);
+ }
+#endif
}
current_enum = prev_enum;
@@ -1133,9 +1142,6 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
ERR_PRINT("Trying to resolve undefined member.");
break;
}
-#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
-#endif
}
parser->current_class = previous_class;
@@ -1146,11 +1152,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
p_source = p_class;
}
+ if (!p_class->resolved_interface) {
#ifdef DEBUG_ENABLED
- bool has_static_data = p_class->has_static_data;
+ bool has_static_data = p_class->has_static_data;
#endif
- if (!p_class->resolved_interface) {
if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
@@ -1178,6 +1184,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
return;
}
+
p_class->resolved_interface = true;
if (resolve_class_inheritance(p_class) != OK) {
@@ -1319,10 +1326,6 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
GDScriptParser::ClassNode::Member member = p_class->members[i];
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- HashSet<GDScriptWarning::Code> previously_ignored_warnings = parser->ignored_warnings;
- for (GDScriptWarning::Code ignored_warning : member.variable->ignored_warnings) {
- parser->ignored_warnings.insert(ignored_warning);
- }
if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
}
@@ -1396,10 +1399,12 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
}
}
}
-
+ } else if (member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
-#endif // DEBUG_ENABLED
+ if (member.signal->usages == 0) {
+ parser->push_warning(member.signal->identifier, GDScriptWarning::UNUSED_SIGNAL, member.signal->identifier->name);
+ }
+#endif
}
}
@@ -1431,6 +1436,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
case GDScriptParser::Node::NONE:
break; // Unreachable.
case GDScriptParser::Node::CLASS:
+ // NOTE: Currently this route is never executed, `resolve_class_*()` is called directly.
if (OK == resolve_class_inheritance(static_cast<GDScriptParser::ClassNode *>(p_node), true)) {
resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node), true);
resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node), true);
@@ -1584,13 +1590,6 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
p_function->resolved_signature = true;
-#ifdef DEBUG_ENABLED
- HashSet<GDScriptWarning::Code> previously_ignored_warnings = parser->ignored_warnings;
- for (GDScriptWarning::Code ignored_warning : p_function->ignored_warnings) {
- parser->ignored_warnings.insert(ignored_warning);
- }
-#endif
-
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
bool previous_static_context = static_context;
@@ -1775,9 +1774,6 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
p_function->set_datatype(prev_datatype);
}
-#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
-#endif
parser->current_function = previous_function;
static_context = previous_static_context;
}
@@ -1788,13 +1784,6 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
}
p_function->resolved_body = true;
-#ifdef DEBUG_ENABLED
- HashSet<GDScriptWarning::Code> previously_ignored_warnings = parser->ignored_warnings;
- for (GDScriptWarning::Code ignored_warning : p_function->ignored_warnings) {
- parser->ignored_warnings.insert(ignored_warning);
- }
-#endif
-
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
@@ -1812,9 +1801,6 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
}
}
-#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
-#endif
parser->current_function = previous_function;
static_context = previous_static_context;
}
@@ -1852,23 +1838,11 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
// Apply annotations.
for (GDScriptParser::AnnotationNode *&E : stmt->annotations) {
resolve_annotation(E);
- E->apply(parser, stmt, nullptr);
+ E->apply(parser, stmt, nullptr); // TODO: Provide `p_class`.
}
-#ifdef DEBUG_ENABLED
- HashSet<GDScriptWarning::Code> previously_ignored_warnings = parser->ignored_warnings;
- for (GDScriptWarning::Code ignored_warning : stmt->ignored_warnings) {
- parser->ignored_warnings.insert(ignored_warning);
- }
-#endif // DEBUG_ENABLED
-
resolve_node(stmt);
resolve_pending_lambda_bodies();
-
-#ifdef DEBUG_ENABLED
- parser->ignored_warnings = previously_ignored_warnings;
-#endif // DEBUG_ENABLED
-
decide_suite_type(p_suite, stmt);
}
}
@@ -2257,6 +2231,12 @@ void GDScriptAnalyzer::resolve_match(GDScriptParser::MatchNode *p_match) {
}
void GDScriptAnalyzer::resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test) {
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : p_match_branch->annotations) {
+ resolve_annotation(E);
+ E->apply(parser, p_match_branch, nullptr); // TODO: Provide `p_class`.
+ }
+
for (int i = 0; i < p_match_branch->patterns.size(); i++) {
resolve_match_pattern(p_match_branch->patterns[i], p_match_test);
}
@@ -3746,6 +3726,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (is_base && !base.is_meta_type) {
p_identifier->set_datatype(member.get_datatype());
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
+ p_identifier->signal_source = member.signal;
+ member.signal->usages += 1;
return;
}
} break;
@@ -3930,6 +3912,8 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
found_source = true;
break;
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
+ p_identifier->signal_source->usages++;
+ [[fallthrough]];
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
mark_lambda_use_self();
break;
@@ -5636,21 +5620,20 @@ Error GDScriptAnalyzer::resolve_dependencies() {
Error GDScriptAnalyzer::analyze() {
parser->errors.clear();
- Error err = OK;
- err = resolve_inheritance();
+ Error err = resolve_inheritance();
if (err) {
return err;
}
- // Apply annotations.
- for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
- resolve_annotation(E);
- E->apply(parser, parser->head, nullptr);
- }
-
resolve_interface();
resolve_body();
+
+#ifdef DEBUG_ENABLED
+ // Apply here, after all `@warning_ignore`s have been resolved and applied.
+ parser->apply_pending_warnings();
+#endif
+
if (!parser->errors.is_empty()) {
return ERR_PARSE_ERROR;
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 49341cb670..8d98c0b11c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -127,7 +127,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
// Warning annotations.
- register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
+ register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
// Networking.
register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("authority", "call_remote", "unreliable", 0));
}
@@ -181,47 +181,62 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
#ifdef DEBUG_ENABLED
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
ERR_FAIL_NULL(p_source);
+ ERR_FAIL_INDEX(p_code, GDScriptWarning::WARNING_MAX);
+
if (is_ignoring_warnings) {
return;
}
if (GLOBAL_GET("debug/gdscript/warnings/exclude_addons").booleanize() && script_path.begins_with("res://addons/")) {
return;
}
-
- if (ignored_warnings.has(p_code)) {
+ GDScriptWarning::WarnLevel warn_level = (GDScriptWarning::WarnLevel)(int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
+ if (warn_level == GDScriptWarning::IGNORE) {
return;
}
- int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
- if (!warn_level) {
- return;
- }
+ PendingWarning pw;
+ pw.source = p_source;
+ pw.code = p_code;
+ pw.treated_as_error = warn_level == GDScriptWarning::ERROR;
+ pw.symbols = p_symbols;
- GDScriptWarning warning;
- warning.code = p_code;
- warning.symbols = p_symbols;
- warning.start_line = p_source->start_line;
- warning.end_line = p_source->end_line;
- warning.leftmost_column = p_source->leftmost_column;
- warning.rightmost_column = p_source->rightmost_column;
+ pending_warnings.push_back(pw);
+}
- if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
- push_error(warning.get_message() + String(" (Warning treated as error.)"), p_source);
- return;
- }
+void GDScriptParser::apply_pending_warnings() {
+ for (const PendingWarning &pw : pending_warnings) {
+ if (warning_ignored_lines[pw.code].has(pw.source->start_line)) {
+ continue;
+ }
- List<GDScriptWarning>::Element *before = nullptr;
- for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
- if (E->get().start_line > warning.start_line) {
- break;
+ GDScriptWarning warning;
+ warning.code = pw.code;
+ warning.symbols = pw.symbols;
+ warning.start_line = pw.source->start_line;
+ warning.end_line = pw.source->end_line;
+ warning.leftmost_column = pw.source->leftmost_column;
+ warning.rightmost_column = pw.source->rightmost_column;
+
+ if (pw.treated_as_error) {
+ push_error(warning.get_message() + String(" (Warning treated as error.)"), pw.source);
+ continue;
+ }
+
+ List<GDScriptWarning>::Element *before = nullptr;
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
+ if (E->get().start_line > warning.start_line) {
+ break;
+ }
+ before = E;
+ }
+ if (before) {
+ warnings.insert_after(before, warning);
+ } else {
+ warnings.push_front(warning);
}
- before = E;
- }
- if (before) {
- warnings.insert_after(before, warning);
- } else {
- warnings.push_front(warning);
}
+
+ pending_warnings.clear();
}
#endif
@@ -553,25 +568,53 @@ void GDScriptParser::end_statement(const String &p_context) {
void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
+ head->start_line = 1;
+ head->end_line = 1;
head->fqcn = GDScript::canonicalize_path(script_path);
current_class = head;
bool can_have_class_or_extends = true;
+#define PUSH_PENDING_ANNOTATIONS_TO_HEAD \
+ if (!annotation_stack.is_empty()) { \
+ for (AnnotationNode * annot : annotation_stack) { \
+ head->annotations.push_back(annot); \
+ } \
+ annotation_stack.clear(); \
+ }
+
while (!check(GDScriptTokenizer::Token::TK_EOF)) {
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STANDALONE);
if (annotation != nullptr) {
- if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
- // `@icon` needs to be applied in the parser. See GH-72444.
- if (annotation->name == SNAME("@icon")) {
- annotation->apply(this, head, nullptr);
+ if (annotation->applies_to(AnnotationInfo::CLASS)) {
+ // We do not know in advance what the annotation will be applied to: the `head` class or the subsequent inner class.
+ // If we encounter `class_name`, `extends` or pure `SCRIPT` annotation, then it's `head`, otherwise it's an inner class.
+ annotation_stack.push_back(annotation);
+ } else if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
+ if (annotation->name == SNAME("@tool") || annotation->name == SNAME("@icon")) {
+ // Some annotations need to be resolved in the parser.
+ annotation->apply(this, head, nullptr); // `head->outer == nullptr`.
} else {
head->annotations.push_back(annotation);
}
+ } else if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after a standalone annotation.)");
+ }
+ if (annotation->name == SNAME("@export_category") || annotation->name == SNAME("@export_group") || annotation->name == SNAME("@export_subgroup")) {
+ head->add_member_group(annotation);
+ // This annotation must appear after script-level annotations and `class_name`/`extends`,
+ // so we stop looking for script-level stuff.
+ can_have_class_or_extends = false;
+ break;
+ } else {
+ // For potential non-group standalone annotations.
+ push_error(R"(Unexpected standalone annotation.)");
+ }
} else {
annotation_stack.push_back(annotation);
- // This annotation must appear after script-level annotations
- // and class_name/extends (ex: could be @onready or @export),
+ // This annotation must appear after script-level annotations and `class_name`/`extends`,
// so we stop looking for script-level stuff.
can_have_class_or_extends = false;
break;
@@ -592,6 +635,10 @@ void GDScriptParser::parse_program() {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
+ if (head->start_line == 1) {
+ reset_extents(head, current);
+ }
advance();
if (head->identifier != nullptr) {
push_error(R"("class_name" can only be used once.)");
@@ -600,6 +647,10 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
+ if (head->start_line == 1) {
+ reset_extents(head, current);
+ }
advance();
if (head->extends_used) {
push_error(R"("extends" can only be used once.)");
@@ -608,6 +659,10 @@ void GDScriptParser::parse_program() {
end_statement("superclass");
}
break;
+ case GDScriptTokenizer::Token::TK_EOF:
+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
+ can_have_class_or_extends = false;
+ break;
case GDScriptTokenizer::Token::LITERAL:
if (current.literal.get_type() == Variant::STRING) {
// Allow strings in class body as multiline comments.
@@ -629,6 +684,8 @@ void GDScriptParser::parse_program() {
}
}
+#undef PUSH_PENDING_ANNOTATIONS_TO_HEAD
+
parse_class_body(true);
complete_extents(head);
@@ -907,8 +964,8 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
case GDScriptTokenizer::Token::ANNOTATION: {
advance();
- // Check for standalone and class-level annotations.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
+ // Check for class-level and standalone annotations.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STANDALONE);
if (annotation != nullptr) {
if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
@@ -918,9 +975,9 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
current_class->add_member_group(annotation);
} else {
// For potential non-group standalone annotations.
- push_error(R"(Unexpected standalone annotation in class body.)");
+ push_error(R"(Unexpected standalone annotation.)");
}
- } else {
+ } else { // `AnnotationInfo::CLASS_LEVEL`.
annotation_stack.push_back(annotation);
}
}
@@ -1342,22 +1399,9 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
break; // Allow trailing comma.
}
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
- EnumNode::Value item;
GDScriptParser::IdentifierNode *identifier = parse_identifier();
-#ifdef DEBUG_ENABLED
- if (!named) { // Named enum identifiers do not shadow anything since you can only access them with NamedEnum.ENUM_VALUE
- for (MethodInfo &info : gdscript_funcs) {
- if (info.name == identifier->name) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
- }
- }
- if (Variant::has_utility_function(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
- } else if (ClassDB::class_exists(identifier->name)) {
- push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
- }
- }
-#endif
+
+ EnumNode::Value item;
item.identifier = identifier;
item.parent_enum = enum_node;
item.line = previous.start_line;
@@ -1701,7 +1745,19 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
bool unreachable = current_suite->has_return && !current_suite->has_unreachable_code;
#endif
- bool is_annotation = false;
+ List<AnnotationNode *> annotations;
+ if (current.type != GDScriptTokenizer::Token::ANNOTATION) {
+ while (!annotation_stack.is_empty()) {
+ AnnotationNode *last_annotation = annotation_stack.back()->get();
+ if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) {
+ annotations.push_front(last_annotation);
+ annotation_stack.pop_back();
+ } else {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a statement.)", last_annotation->name));
+ clear_unused_annotations();
+ }
+ }
+ }
switch (current.type) {
case GDScriptTokenizer::Token::PASS:
@@ -1775,7 +1831,6 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
break;
case GDScriptTokenizer::Token::ANNOTATION: {
advance();
- is_annotation = true;
AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
if (annotation != nullptr) {
annotation_stack.push_back(annotation);
@@ -1804,10 +1859,9 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
#ifdef DEBUG_ENABLED
if (expression != nullptr) {
switch (expression->type) {
- case Node::CALL:
case Node::ASSIGNMENT:
case Node::AWAIT:
- case Node::TERNARY_OPERATOR:
+ case Node::CALL:
// Fine.
break;
case Node::LAMBDA:
@@ -1815,11 +1869,14 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
break;
case Node::LITERAL:
- if (static_cast<GDScriptParser::LiteralNode *>(expression)->value.get_type() == Variant::STRING) {
- // Allow strings as multiline comments.
- break;
+ // Allow strings as multiline comments.
+ if (static_cast<GDScriptParser::LiteralNode *>(expression)->value.get_type() != Variant::STRING) {
+ push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
- [[fallthrough]];
+ break;
+ case Node::TERNARY_OPERATOR:
+ push_warning(expression, GDScriptWarning::STANDALONE_TERNARY);
+ break;
default:
push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
@@ -1829,14 +1886,9 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
}
}
- while (!is_annotation && result != nullptr && !annotation_stack.is_empty()) {
- AnnotationNode *last_annotation = annotation_stack.back()->get();
- if (last_annotation->applies_to(AnnotationInfo::STATEMENT)) {
- result->annotations.push_front(last_annotation);
- annotation_stack.pop_back();
- } else {
- push_error(vformat(R"(Annotation "%s" cannot be applied to a statement.)", last_annotation->name));
- clear_unused_annotations();
+ if (result != nullptr && !annotations.is_empty()) {
+ for (AnnotationNode *&annotation : annotations) {
+ result->annotations.push_back(annotation);
}
}
@@ -2017,10 +2069,10 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
}
GDScriptParser::MatchNode *GDScriptParser::parse_match() {
- MatchNode *match = alloc_node<MatchNode>();
+ MatchNode *match_node = alloc_node<MatchNode>();
- match->test = parse_expression(false);
- if (match->test == nullptr) {
+ match_node->test = parse_expression(false);
+ if (match_node->test == nullptr) {
push_error(R"(Expected expression to test after "match".)");
}
@@ -2028,20 +2080,45 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
- complete_extents(match);
- return match;
+ complete_extents(match_node);
+ return match_node;
}
bool all_have_return = true;
bool have_wildcard = false;
+ List<AnnotationNode *> match_branch_annotation_stack;
+
while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
+ if (match(GDScriptTokenizer::Token::PASS)) {
+ consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected newline after "pass".)");
+ continue;
+ }
+
+ if (match(GDScriptTokenizer::Token::ANNOTATION)) {
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::STATEMENT);
+ if (annotation == nullptr) {
+ continue;
+ }
+ if (annotation->name != SNAME("@warning_ignore")) {
+ push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name), annotation);
+ continue;
+ }
+ match_branch_annotation_stack.push_back(annotation);
+ continue;
+ }
+
MatchBranchNode *branch = parse_match_branch();
if (branch == nullptr) {
advance();
continue;
}
+ for (AnnotationNode *annotation : match_branch_annotation_stack) {
+ branch->annotations.push_back(annotation);
+ }
+ match_branch_annotation_stack.clear();
+
#ifdef DEBUG_ENABLED
if (have_wildcard && !branch->patterns.is_empty()) {
push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN);
@@ -2050,9 +2127,9 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
have_wildcard = have_wildcard || branch->has_wildcard;
all_have_return = all_have_return && branch->block->has_return;
- match->branches.push_back(branch);
+ match_node->branches.push_back(branch);
}
- complete_extents(match);
+ complete_extents(match_node);
consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
@@ -2060,7 +2137,12 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
current_suite->has_return = true;
}
- return match;
+ for (const AnnotationNode *annotation : match_branch_annotation_stack) {
+ push_error(vformat(R"(Annotation "%s" does not precede a valid target, so it will have no effect.)", annotation->name), annotation);
+ }
+ match_branch_annotation_stack.clear();
+
+ return match_node;
}
GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
@@ -2378,7 +2460,7 @@ GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
IdentifierNode *identifier = static_cast<IdentifierNode *>(parse_identifier(nullptr, false));
#ifdef DEBUG_ENABLED
// Check for spoofing here (if available in TextServer) since this isn't called inside expressions. This is only relevant for declarations.
- if (identifier && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier->name.operator String())) {
+ if (identifier && TS->has_feature(TextServer::FEATURE_UNICODE_SECURITY) && TS->spoof_check(identifier->name)) {
push_warning(identifier, GDScriptWarning::CONFUSABLE_IDENTIFIER, identifier->name.operator String());
}
#endif
@@ -3161,6 +3243,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
complete_extents(get_node);
return nullptr;
}
+
get_node->full_path += "%";
path_state = PATH_STATE_PERCENT;
@@ -3204,6 +3287,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
path_state = PATH_STATE_NODE_NAME;
} else if (current.is_node_name()) {
advance();
+
String identifier = previous.get_identifier();
#ifdef DEBUG_ENABLED
// Check spoofing.
@@ -3934,7 +4018,7 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
return false;
}
- // `@icon`'s argument needs to be resolved in the parser. See GH-72444.
+ // Some annotations need to be resolved in the parser.
if (p_annotation->name == SNAME("@icon")) {
ExpressionNode *argument = p_annotation->arguments[0];
@@ -4401,23 +4485,77 @@ bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation
}
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
-#ifdef DEBUG_ENABLED
+#ifndef DEBUG_ENABLED
+ // Only available in debug builds.
+ return true;
+#else // DEBUG_ENABLED
+ if (is_ignoring_warnings) {
+ return true; // We already ignore all warnings, let's optimize it.
+ }
+
bool has_error = false;
for (const Variant &warning_name : p_annotation->resolved_arguments) {
- GDScriptWarning::Code warning = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());
- if (warning == GDScriptWarning::WARNING_MAX) {
+ GDScriptWarning::Code warning_code = GDScriptWarning::get_code_from_name(String(warning_name).to_upper());
+ if (warning_code == GDScriptWarning::WARNING_MAX) {
push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation);
has_error = true;
} else {
- p_target->ignored_warnings.push_back(warning);
+ int start_line = p_annotation->start_line;
+ int end_line = p_target->end_line;
+
+ switch (p_target->type) {
+#define SIMPLE_CASE(m_type, m_class, m_property) \
+ case m_type: { \
+ m_class *node = static_cast<m_class *>(p_target); \
+ if (node->m_property == nullptr) { \
+ end_line = node->start_line; \
+ } else { \
+ end_line = node->m_property->end_line; \
+ } \
+ } break;
+
+ // Can contain properties (set/get).
+ SIMPLE_CASE(Node::VARIABLE, VariableNode, initializer)
+
+ // Contain bodies.
+ SIMPLE_CASE(Node::FOR, ForNode, list)
+ SIMPLE_CASE(Node::IF, IfNode, condition)
+ SIMPLE_CASE(Node::MATCH, MatchNode, test)
+ SIMPLE_CASE(Node::WHILE, WhileNode, condition)
+#undef SIMPLE_CASE
+
+ case Node::CLASS: {
+ end_line = p_target->start_line;
+ for (const AnnotationNode *annotation : p_target->annotations) {
+ start_line = MIN(start_line, annotation->start_line);
+ end_line = MAX(end_line, annotation->end_line);
+ }
+ } break;
+
+ case Node::FUNCTION: {
+ // `@warning_ignore` on function has a controversial feature that is used in tests.
+ // It's better not to remove it for now, while there is no way to mass-ignore warnings.
+ } break;
+
+ case Node::MATCH_BRANCH: {
+ MatchBranchNode *branch = static_cast<MatchBranchNode *>(p_target);
+ end_line = branch->start_line;
+ for (int i = 0; i < branch->patterns.size(); i++) {
+ end_line = MAX(end_line, branch->patterns[i]->end_line);
+ }
+ } break;
+
+ default: {
+ } break;
+ }
+
+ end_line = MAX(start_line, end_line); // Prevent infinite loop.
+ for (int line = start_line; line <= end_line; line++) {
+ warning_ignored_lines[warning_code].insert(line);
+ }
}
}
-
return !has_error;
-
-#else // ! DEBUG_ENABLED
- // Only available in debug builds.
- return true;
#endif // DEBUG_ENABLED
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 45ab3f3e38..583b60bf16 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -338,9 +338,6 @@ public:
int leftmost_column = 0, rightmost_column = 0;
Node *next = nullptr;
List<AnnotationNode *> annotations;
-#ifdef DEBUG_ENABLED
- Vector<GDScriptWarning::Code> ignored_warnings;
-#endif
DataType datatype;
@@ -900,9 +897,10 @@ public:
union {
ParameterNode *parameter_source = nullptr;
- ConstantNode *constant_source;
- VariableNode *variable_source;
IdentifierNode *bind_source;
+ VariableNode *variable_source;
+ ConstantNode *constant_source;
+ SignalNode *signal_source;
};
FunctionNode *source_function = nullptr;
@@ -1051,6 +1049,8 @@ public:
MemberDocData doc_data;
#endif // TOOLS_ENABLED
+ int usages = 0;
+
SignalNode() {
type = SIGNAL;
}
@@ -1334,9 +1334,17 @@ private:
List<ParserError> errors;
#ifdef DEBUG_ENABLED
+ struct PendingWarning {
+ const Node *source = nullptr;
+ GDScriptWarning::Code code = GDScriptWarning::WARNING_MAX;
+ bool treated_as_error = false;
+ Vector<String> symbols;
+ };
+
bool is_ignoring_warnings = false;
List<GDScriptWarning> warnings;
- HashSet<GDScriptWarning::Code> ignored_warnings;
+ List<PendingWarning> pending_warnings;
+ HashSet<int> warning_ignored_lines[GDScriptWarning::WARNING_MAX];
HashSet<int> unsafe_lines;
#endif
@@ -1368,7 +1376,7 @@ private:
FUNCTION = 1 << 5,
STATEMENT = 1 << 6,
STANDALONE = 1 << 7,
- CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
+ CLASS_LEVEL = CLASS | VARIABLE | CONSTANT | SIGNAL | FUNCTION,
};
uint32_t target_kind = 0; // Flags.
AnnotationAction apply = nullptr;
@@ -1438,6 +1446,7 @@ private:
void push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Symbols &...p_symbols) {
push_warning(p_source, p_code, Vector<String>{ p_symbols... });
}
+ void apply_pending_warnings();
#endif
void make_completion_context(CompletionType p_type, Node *p_node, int p_argument = -1, bool p_force = false);
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 1fe9b0425c..e7d9787eab 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -52,13 +52,13 @@ String GDScriptWarning::get_message() const {
return vformat(R"(The local constant "%s" is declared but never used in the block. If this is intended, prefix it with an underscore: "_%s".)", symbols[0], symbols[0]);
case UNUSED_PRIVATE_CLASS_VARIABLE:
CHECK_SYMBOLS(1);
- return vformat(R"(The class variable "%s" is declared but never used in the script.)", symbols[0]);
+ return vformat(R"(The class variable "%s" is declared but never used in the class.)", symbols[0]);
case UNUSED_PARAMETER:
CHECK_SYMBOLS(2);
return vformat(R"*(The parameter "%s" is never used in the function "%s()". If this is intended, prefix it with an underscore: "_%s".)*", symbols[1], symbols[0], symbols[1]);
case UNUSED_SIGNAL:
CHECK_SYMBOLS(1);
- return vformat(R"(The signal "%s" is declared but never emitted.)", symbols[0]);
+ return vformat(R"(The signal "%s" is declared but never explicitly used in the class.)", symbols[0]);
case SHADOWED_VARIABLE:
CHECK_SYMBOLS(4);
return vformat(R"(The local %s "%s" is shadowing an already-declared %s at line %s.)", symbols[0], symbols[1], symbols[2], symbols[3]);
@@ -76,18 +76,9 @@ String GDScriptWarning::get_message() const {
case STANDALONE_EXPRESSION:
return "Standalone expression (the line has no effect).";
case STANDALONE_TERNARY:
- return "Standalone ternary conditional operator: the return value is being discarded.";
+ return "Standalone ternary operator: the return value is being discarded.";
case INCOMPATIBLE_TERNARY:
- return "Values of the ternary conditional are not mutually compatible.";
- case PROPERTY_USED_AS_FUNCTION:
- CHECK_SYMBOLS(2);
- return vformat(R"*(The method "%s()" was not found in base "%s" but there's a property with the same name. Did you mean to access it?)*", symbols[0], symbols[1]);
- case CONSTANT_USED_AS_FUNCTION:
- CHECK_SYMBOLS(2);
- return vformat(R"*(The method "%s()" was not found in base "%s" but there's a constant with the same name. Did you mean to access it?)*", symbols[0], symbols[1]);
- case FUNCTION_USED_AS_PROPERTY:
- CHECK_SYMBOLS(2);
- return vformat(R"(The property "%s" was not found in base "%s" but there's a method with the same name. Did you mean to call it?)", symbols[0], symbols[1]);
+ return "Values of the ternary operator are not mutually compatible.";
case UNTYPED_DECLARATION:
CHECK_SYMBOLS(2);
if (symbols[0] == "Function") {
@@ -162,10 +153,17 @@ String GDScriptWarning::get_message() const {
return vformat(R"*(The default value is using "%s" which won't return nodes in the scene tree before "_ready()" is called. Use the "@onready" annotation to solve this.)*", symbols[0]);
case ONREADY_WITH_EXPORT:
return R"("@onready" will set the default value after "@export" takes effect and will override it.)";
+#ifndef DISABLE_DEPRECATED
+ // Never produced. These warnings migrated from 3.x by mistake.
+ case PROPERTY_USED_AS_FUNCTION: // There is already an error.
+ case CONSTANT_USED_AS_FUNCTION: // There is already an error.
+ case FUNCTION_USED_AS_PROPERTY: // This is valid, returns `Callable`.
+ break;
+#endif
case WARNING_MAX:
- break; // Can't happen, but silences warning
+ break; // Can't happen, but silences warning.
}
- ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + ".");
+ ERR_FAIL_V_MSG(String(), vformat(R"(Invalid GDScript warning "%s".)", get_name_from_code(code)));
#undef CHECK_SYMBOLS
}
@@ -206,9 +204,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"STANDALONE_EXPRESSION",
"STANDALONE_TERNARY",
"INCOMPATIBLE_TERNARY",
- "PROPERTY_USED_AS_FUNCTION",
- "CONSTANT_USED_AS_FUNCTION",
- "FUNCTION_USED_AS_PROPERTY",
"UNTYPED_DECLARATION",
"INFERRED_DECLARATION",
"UNSAFE_PROPERTY_ACCESS",
@@ -236,6 +231,11 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"NATIVE_METHOD_OVERRIDE",
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
"ONREADY_WITH_EXPORT",
+#ifndef DISABLE_DEPRECATED
+ "PROPERTY_USED_AS_FUNCTION",
+ "CONSTANT_USED_AS_FUNCTION",
+ "FUNCTION_USED_AS_PROPERTY",
+#endif
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 1aef6fa81b..69cc8c179f 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -50,9 +50,9 @@ public:
UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc).
UNUSED_VARIABLE, // Local variable is declared but never used.
UNUSED_LOCAL_CONSTANT, // Local constant is declared but never used.
- UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the file.
+ UNUSED_PRIVATE_CLASS_VARIABLE, // Class variable is declared private ("_" prefix) but never used in the class.
UNUSED_PARAMETER, // Function parameter is never used.
- UNUSED_SIGNAL, // Signal is defined but never emitted.
+ UNUSED_SIGNAL, // Signal is defined but never explicitly used in the class.
SHADOWED_VARIABLE, // Variable name shadowed by other variable in same class.
SHADOWED_VARIABLE_BASE_CLASS, // Variable name shadowed by other variable in some base class.
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
@@ -61,9 +61,6 @@ public:
STANDALONE_EXPRESSION, // Expression not assigned to a variable.
STANDALONE_TERNARY, // Return value of ternary expression is discarded.
INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
- PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
- CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
- FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name.
UNTYPED_DECLARATION, // Variable/parameter/function has no static type, explicitly specified or implicitly inferred.
INFERRED_DECLARATION, // Variable/constant/parameter has an implicitly inferred static type.
UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes).
@@ -91,6 +88,11 @@ public:
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation.
ONREADY_WITH_EXPORT, // The `@onready` annotation will set the value after `@export` which is likely not intended.
+#ifndef DISABLE_DEPRECATED
+ PROPERTY_USED_AS_FUNCTION, // Function not found, but there's a property with the same name.
+ CONSTANT_USED_AS_FUNCTION, // Function not found, but there's a constant with the same name.
+ FUNCTION_USED_AS_PROPERTY, // Property not found, but there's a function with the same name.
+#endif
WARNING_MAX,
};
@@ -110,9 +112,6 @@ public:
WARN, // STANDALONE_EXPRESSION
WARN, // STANDALONE_TERNARY
WARN, // INCOMPATIBLE_TERNARY
- WARN, // PROPERTY_USED_AS_FUNCTION
- WARN, // CONSTANT_USED_AS_FUNCTION
- WARN, // FUNCTION_USED_AS_PROPERTY
IGNORE, // UNTYPED_DECLARATION // Static typing is optional, we don't want to spam warnings.
IGNORE, // INFERRED_DECLARATION // Static typing is optional, we don't want to spam warnings.
IGNORE, // UNSAFE_PROPERTY_ACCESS // Too common in untyped scenarios.
@@ -140,6 +139,11 @@ public:
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
ERROR, // ONREADY_WITH_EXPORT // May not work as expected.
+#ifndef DISABLE_DEPRECATED
+ WARN, // PROPERTY_USED_AS_FUNCTION
+ WARN, // CONSTANT_USED_AS_FUNCTION
+ WARN, // FUNCTION_USED_AS_PROPERTY
+#endif
};
static_assert((sizeof(default_warning_levels) / sizeof(default_warning_levels[0])) == WARNING_MAX, "Amount of default levels does not match the amount of warnings.");
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd
deleted file mode 100644
index 292db30bcd..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.gd
+++ /dev/null
@@ -1,15 +0,0 @@
-@warning_ignore("unused_private_class_variable")
-var _unused = 2
-
-@warning_ignore("unused_variable")
-func test():
- print("test")
- var unused = 3
-
- @warning_ignore("redundant_await")
- print(await regular_func())
-
- print("done")
-
-func regular_func() -> int:
- return 0
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.out b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.out
deleted file mode 100644
index 42292774a0..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_annotation.out
+++ /dev/null
@@ -1,4 +0,0 @@
-GDTEST_OK
-test
-0
-done
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.gd
new file mode 100644
index 0000000000..10eca33647
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.gd
@@ -0,0 +1,35 @@
+@warning_ignore("confusable_identifier")
+class MyClАss:
+ var my_vАr
+
+@warning_ignore("narrowing_conversion")
+var i: int = f:
+ get:
+ return f
+
+var f: float
+
+@warning_ignore("narrowing_conversion")
+func test_func(_i: int = f):
+ i = f
+
+func test():
+ @warning_ignore("narrowing_conversion")
+ if signi(f): # TODO: Allow `@warning_ignore` before `elif`?
+ i = f
+
+ @warning_ignore("narrowing_conversion")
+ match signi(f):
+ 1:
+ i = f
+ @warning_ignore("confusable_identifier")
+ var my_vАr:
+ var _my_vАr: Variant = my_vАr
+
+ @warning_ignore("narrowing_conversion")
+ for j in signi(f):
+ i = f
+
+ @warning_ignore("narrowing_conversion")
+ while signi(f):
+ i = f
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out
new file mode 100644
index 0000000000..032af0322b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_targets.out
@@ -0,0 +1,29 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> CONFUSABLE_IDENTIFIER
+>> The identifier "my_vАr" has misleading characters and might be confused with something else.
+>> WARNING
+>> Line: 8
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 19
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 24
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 27
+>> CONFUSABLE_IDENTIFIER
+>> The identifier "_my_vАr" has misleading characters and might be confused with something else.
+>> WARNING
+>> Line: 31
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
+>> WARNING
+>> Line: 35
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd
new file mode 100644
index 0000000000..8a1ab6f406
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd
@@ -0,0 +1,156 @@
+@warning_ignore("redundant_static_unload")
+@static_unload
+extends Node
+
+class A extends Node:
+ static func static_called_on_instance():
+ pass
+
+ @warning_ignore("get_node_default_without_onready")
+ var get_node_default_without_onready = $Node
+
+@warning_ignore("unused_private_class_variable")
+var _unused_private_class_variable
+
+@warning_ignore("onready_with_export")
+@onready @export var onready_with_export = 1
+
+var shadowed_variable
+var confusable_local_usage
+
+@warning_ignore("unused_signal")
+signal unused_signal()
+
+func variant_func() -> Variant:
+ return null
+
+func int_func() -> int:
+ return 1
+
+@warning_ignore("unused_parameter")
+func test_warnings(unused_private_class_variable):
+ var t = 1
+
+ @warning_ignore("unassigned_variable")
+ var unassigned_variable
+ print(unassigned_variable)
+
+ var _unassigned_variable_op_assign
+ @warning_ignore("unassigned_variable_op_assign")
+ _unassigned_variable_op_assign += t
+
+ @warning_ignore("unused_variable")
+ var unused_variable
+
+ @warning_ignore("unused_local_constant")
+ const unused_local_constant = 1
+
+ @warning_ignore("shadowed_variable")
+ var shadowed_variable = 1
+ print(shadowed_variable)
+
+ @warning_ignore("shadowed_variable_base_class")
+ var name = "test"
+ print(name)
+
+ @warning_ignore("shadowed_global_identifier")
+ var var_to_str = 1
+ print(var_to_str)
+
+ @warning_ignore("standalone_expression")
+ 1 + 2
+
+ @warning_ignore("standalone_ternary")
+ 1 if 2 else 3
+
+ @warning_ignore("incompatible_ternary")
+ t = 1 if 2 else false
+
+ @warning_ignore("unsafe_property_access")
+ self.unsafe_property_access = 1
+
+ var node: Node = null
+ @warning_ignore("unsafe_method_access")
+ node.unsafe_method_access()
+
+ @warning_ignore("unsafe_cast")
+ print(variant_func().x as int)
+
+ var key: Variant = "key"
+ @warning_ignore("unsafe_call_argument")
+ set(key, 1)
+
+ variant_func() # No warning (intended?).
+ @warning_ignore("return_value_discarded")
+ int_func()
+
+ var a: A = null
+ @warning_ignore("static_called_on_instance")
+ a.static_called_on_instance()
+
+ @warning_ignore("redundant_await")
+ await 1
+
+ @warning_ignore("assert_always_true")
+ assert(true)
+
+ assert(false) # No warning (intended).
+ @warning_ignore("assert_always_false")
+ assert(false and false)
+
+ @warning_ignore("integer_division")
+ var _integer_division = 5 / 2
+
+ @warning_ignore("narrowing_conversion")
+ var _narrowing_conversion: int = floorf(2.5)
+
+ @warning_ignore("int_as_enum_without_cast")
+ var _int_as_enum_without_cast: Variant.Type = 1
+
+ @warning_ignore("int_as_enum_without_cast", "int_as_enum_without_match")
+ var _int_as_enum_without_match: Variant.Type = 255
+
+ @warning_ignore("confusable_identifier")
+ var _cОnfusable_identifier = 1
+
+ if true:
+ @warning_ignore("confusable_local_declaration")
+ var _confusable_local_declaration = 1
+ var _confusable_local_declaration = 2
+
+ @warning_ignore("confusable_local_usage")
+ print(confusable_local_usage)
+ @warning_ignore("shadowed_variable")
+ var confusable_local_usage = 2
+ print(confusable_local_usage)
+
+ @warning_ignore("inference_on_variant")
+ var _inference_on_variant := variant_func()
+
+func test_unreachable_code():
+ return
+ @warning_ignore("unreachable_code")
+ print(1)
+
+func test_unreachable_pattern():
+ match 1:
+ _:
+ print(0)
+ @warning_ignore("unreachable_pattern")
+ 1:
+ print(1)
+
+func test_unsafe_void_return_variant() -> void:
+ return variant_func() # No warning (intended?).
+
+func test_unsafe_void_return() -> void:
+ @warning_ignore("unsafe_method_access", "unsafe_void_return")
+ return variant_func().f()
+
+@warning_ignore("native_method_override")
+func get_class():
+ pass
+
+# We don't want to execute it because of errors, just analyze.
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.out b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out
index fd88d23950..c902676a5d 100644
--- a/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_private_class_variable.out
@@ -2,8 +2,8 @@ GDTEST_OK
>> WARNING
>> Line: 3
>> UNUSED_PRIVATE_CLASS_VARIABLE
->> The class variable "_a" is declared but never used in the script.
+>> The class variable "_a" is declared but never used in the class.
>> WARNING
>> Line: 7
>> UNUSED_PRIVATE_CLASS_VARIABLE
->> The class variable "_d" is declared but never used in the script.
+>> The class variable "_d" is declared but never used in the class.
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.gd b/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.gd
new file mode 100644
index 0000000000..d937dfdcfe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.gd
@@ -0,0 +1,12 @@
+signal s1()
+signal s2()
+signal s3()
+@warning_ignore("unused_signal")
+signal s4()
+
+func no_exec():
+ s1.emit()
+ print(s2)
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.out b/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.out
new file mode 100644
index 0000000000..ff57017830
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/warnings/unused_signal.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> UNUSED_SIGNAL
+>> The signal "s3" is declared but never explicitly used in the class.
diff --git a/modules/gdscript/tests/scripts/parser/errors/duplicate_tool.out b/modules/gdscript/tests/scripts/parser/errors/duplicate_tool.out
index 26fe23fb78..497d361204 100644
--- a/modules/gdscript/tests/scripts/parser/errors/duplicate_tool.out
+++ b/modules/gdscript/tests/scripts/parser/errors/duplicate_tool.out
@@ -1,2 +1,2 @@
-GDTEST_ANALYZER_ERROR
+GDTEST_PARSER_ERROR
"@tool" annotation can only be used once.
diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd
index eb392672eb..2e1407237f 100644
--- a/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd
+++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance_access.gd
@@ -4,6 +4,7 @@ class Parent:
var parent_variable := 2
+ @warning_ignore("unused_signal")
signal parent_signal
var parent_attribute: int:
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
index f16c768f7f..46b6856d22 100644
--- a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
@@ -14,6 +14,7 @@ func test():
print(v)
print()
+ @warning_ignore("standalone_ternary")
v=func(): print(2) if false else print(3)
@warning_ignore("unsafe_cast")
(v as Callable).call()
diff --git a/modules/gdscript/tests/scripts/parser/features/match.gd b/modules/gdscript/tests/scripts/parser/features/match.gd
index 59b5ba2426..a2e93c64fd 100644
--- a/modules/gdscript/tests/scripts/parser/features/match.gd
+++ b/modules/gdscript/tests/scripts/parser/features/match.gd
@@ -14,3 +14,6 @@ func test():
print("This won't match")
_:
print("This will match")
+
+ match 0:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd
index e4d6a72f90..d02f82d417 100644
--- a/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd
+++ b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd
@@ -1,5 +1,3 @@
-#GDTEST_OK
-
# No parentheses.
signal a
@@ -16,5 +14,15 @@ signal d(
c,
)
+# With type hints.
+signal e(a: int, b: Variant, c: Node)
+
+func no_exec():
+ a.emit()
+ b.emit()
+ c.emit()
+ d.emit()
+ e.emit()
+
func test():
print("Ok")
diff --git a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out
index 7d1558c6fc..ff3e827255 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out
+++ b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out
@@ -2,4 +2,4 @@ GDTEST_OK
>> WARNING
>> Line: 8
>> INCOMPATIBLE_TERNARY
->> Values of the ternary conditional are not mutually compatible.
+>> Values of the ternary operator are not mutually compatible.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.gd
new file mode 100644
index 0000000000..9b296e02e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.gd
@@ -0,0 +1,3 @@
+func test():
+ 1 if true else 2
+ print(1) if true else print(2)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.out
new file mode 100644
index 0000000000..477449e0e3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_ternary.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> STANDALONE_TERNARY
+>> Standalone ternary operator: the return value is being discarded.
+>> WARNING
+>> Line: 3
+>> STANDALONE_TERNARY
+>> Standalone ternary operator: the return value is being discarded.
+1
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info.gd b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
index 6fe9647b4d..d7485f49e6 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info.gd
@@ -56,6 +56,16 @@ signal test_signal_6(a: Resource, b: Array[Resource])
signal test_signal_7(a: TestMemberInfo, b: Array[TestMemberInfo])
signal test_signal_8(a: MyClass, b: Array[MyClass])
+func no_exec():
+ test_signal_1.emit()
+ test_signal_2.emit()
+ test_signal_3.emit()
+ test_signal_4.emit()
+ test_signal_5.emit()
+ test_signal_6.emit()
+ test_signal_7.emit()
+ test_signal_8.emit()
+
func test():
var script: Script = get_script()
for property in script.get_property_list():
diff --git a/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd
index 563c6ce569..ee5c1e1267 100644
--- a/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/member_info_inheritance.gd
@@ -11,7 +11,9 @@ class A:
static func test_static_func_a2(): pass
func test_func_a1(): pass
func test_func_a2(): pass
+ @warning_ignore("unused_signal")
signal test_signal_a1()
+ @warning_ignore("unused_signal")
signal test_signal_a2()
class B extends A:
@@ -23,7 +25,9 @@ class B extends A:
static func test_static_func_b2(): pass
func test_func_b1(): pass
func test_func_b2(): pass
+ @warning_ignore("unused_signal")
signal test_signal_b1()
+ @warning_ignore("unused_signal")
signal test_signal_b2()
func test():
diff --git a/modules/interactive_music/SCsub b/modules/interactive_music/SCsub
new file mode 100644
index 0000000000..2950a30854
--- /dev/null
+++ b/modules/interactive_music/SCsub
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_interactive_music = env_modules.Clone()
+
+# Godot's own source files
+env_interactive_music.add_source_files(env.modules_sources, "*.cpp")
+if env.editor_build:
+ env_interactive_music.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/interactive_music/audio_stream_interactive.cpp b/modules/interactive_music/audio_stream_interactive.cpp
new file mode 100644
index 0000000000..ddac458463
--- /dev/null
+++ b/modules/interactive_music/audio_stream_interactive.cpp
@@ -0,0 +1,1030 @@
+/**************************************************************************/
+/* audio_stream_interactive.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "audio_stream_interactive.h"
+
+#include "core/math/math_funcs.h"
+#include "core/string/print_string.h"
+
+AudioStreamInteractive::AudioStreamInteractive() {
+}
+
+Ref<AudioStreamPlayback> AudioStreamInteractive::instantiate_playback() {
+ Ref<AudioStreamPlaybackInteractive> playback_transitioner;
+ playback_transitioner.instantiate();
+ playback_transitioner->stream = Ref<AudioStreamInteractive>(this);
+ return playback_transitioner;
+}
+
+String AudioStreamInteractive::get_stream_name() const {
+ return "Transitioner";
+}
+
+void AudioStreamInteractive::set_clip_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0 || p_count > MAX_CLIPS);
+
+ AudioServer::get_singleton()->lock();
+
+ if (p_count < clip_count) {
+ // Removing should stop players.
+ version++;
+ }
+
+#ifdef TOOLS_ENABLED
+ stream_name_cache = "";
+ if (p_count < clip_count) {
+ for (int i = 0; i < clip_count; i++) {
+ if (clips[i].auto_advance_next_clip >= p_count) {
+ clips[i].auto_advance_next_clip = 0;
+ clips[i].auto_advance = AUTO_ADVANCE_DISABLED;
+ }
+ }
+
+ for (KeyValue<TransitionKey, Transition> &K : transition_map) {
+ if (K.value.filler_clip >= p_count) {
+ K.value.use_filler_clip = false;
+ K.value.filler_clip = 0;
+ }
+ }
+ if (initial_clip >= p_count) {
+ initial_clip = 0;
+ }
+ }
+#endif
+ clip_count = p_count;
+ AudioServer::get_singleton()->unlock();
+
+ notify_property_list_changed();
+ emit_signal(SNAME("parameter_list_changed"));
+}
+
+void AudioStreamInteractive::set_initial_clip(int p_clip) {
+ ERR_FAIL_INDEX(p_clip, clip_count);
+ initial_clip = p_clip;
+}
+
+int AudioStreamInteractive::get_initial_clip() const {
+ return initial_clip;
+}
+
+int AudioStreamInteractive::get_clip_count() const {
+ return clip_count;
+}
+
+void AudioStreamInteractive::set_clip_name(int p_clip, const StringName &p_name) {
+ ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
+ clips[p_clip].name = p_name;
+}
+
+StringName AudioStreamInteractive::get_clip_name(int p_clip) const {
+ ERR_FAIL_COND_V(p_clip < -1 || p_clip >= MAX_CLIPS, StringName());
+ if (p_clip == CLIP_ANY) {
+ return RTR("All Clips");
+ }
+ return clips[p_clip].name;
+}
+
+void AudioStreamInteractive::set_clip_stream(int p_clip, const Ref<AudioStream> &p_stream) {
+ ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
+ AudioServer::get_singleton()->lock();
+ if (clips[p_clip].stream.is_valid()) {
+ version++;
+ }
+ clips[p_clip].stream = p_stream;
+ AudioServer::get_singleton()->unlock();
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ if (clips[p_clip].name == StringName() && p_stream.is_valid()) {
+ String n;
+ if (!clips[p_clip].stream->get_name().is_empty()) {
+ n = clips[p_clip].stream->get_name().replace(",", " ");
+ } else if (clips[p_clip].stream->get_path().is_resource_file()) {
+ n = clips[p_clip].stream->get_path().get_file().get_basename().replace(",", " ");
+ n = n.capitalize();
+ }
+
+ if (n != "") {
+ clips[p_clip].name = n;
+ }
+ }
+ }
+#endif
+
+#ifdef TOOLS_ENABLED
+ stream_name_cache = "";
+ notify_property_list_changed(); // Hints change if stream changes.
+ emit_signal(SNAME("parameter_list_changed"));
+#endif
+}
+
+Ref<AudioStream> AudioStreamInteractive::get_clip_stream(int p_clip) const {
+ ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, Ref<AudioStream>());
+ return clips[p_clip].stream;
+}
+
+void AudioStreamInteractive::set_clip_auto_advance(int p_clip, AutoAdvanceMode p_mode) {
+ ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
+ ERR_FAIL_INDEX(p_mode, 3);
+ clips[p_clip].auto_advance = p_mode;
+ notify_property_list_changed();
+}
+
+AudioStreamInteractive::AutoAdvanceMode AudioStreamInteractive::get_clip_auto_advance(int p_clip) const {
+ ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, AUTO_ADVANCE_DISABLED);
+ return clips[p_clip].auto_advance;
+}
+
+void AudioStreamInteractive::set_clip_auto_advance_next_clip(int p_clip, int p_index) {
+ ERR_FAIL_INDEX(p_clip, MAX_CLIPS);
+ clips[p_clip].auto_advance_next_clip = p_index;
+}
+
+int AudioStreamInteractive::get_clip_auto_advance_next_clip(int p_clip) const {
+ ERR_FAIL_INDEX_V(p_clip, MAX_CLIPS, -1);
+ return clips[p_clip].auto_advance_next_clip;
+}
+
+// TRANSITIONS
+
+void AudioStreamInteractive::_set_transitions(const Dictionary &p_transitions) {
+ List<Variant> keys;
+ p_transitions.get_key_list(&keys);
+ for (const Variant &K : keys) {
+ Vector2i k = K;
+ Dictionary data = p_transitions[K];
+ ERR_CONTINUE(!data.has("from_time"));
+ ERR_CONTINUE(!data.has("to_time"));
+ ERR_CONTINUE(!data.has("fade_mode"));
+ ERR_CONTINUE(!data.has("fade_beats"));
+ bool use_filler_clip = false;
+ int filler_clip = 0;
+ if (data.has("use_filler_clip") && data.has("filler_clip")) {
+ use_filler_clip = data["use_filler_clip"];
+ filler_clip = data["filler_clip"];
+ }
+ bool hold_previous = data.has("hold_previous") ? bool(data["hold_previous"]) : false;
+
+ add_transition(k.x, k.y, TransitionFromTime(int(data["from_time"])), TransitionToTime(int(data["to_time"])), FadeMode(int(data["fade_mode"])), data["fade_beats"], use_filler_clip, filler_clip, hold_previous);
+ }
+}
+
+Dictionary AudioStreamInteractive::_get_transitions() const {
+ Vector<Vector2i> keys;
+
+ for (const KeyValue<TransitionKey, Transition> &K : transition_map) {
+ keys.push_back(Vector2i(K.key.from_clip, K.key.to_clip));
+ }
+ keys.sort();
+ Dictionary ret;
+ for (int i = 0; i < keys.size(); i++) {
+ const Transition &tr = transition_map[TransitionKey(keys[i].x, keys[i].y)];
+ Dictionary data;
+ data["from_time"] = tr.from_time;
+ data["to_time"] = tr.to_time;
+ data["fade_mode"] = tr.fade_mode;
+ data["fade_beats"] = tr.fade_beats;
+ if (tr.use_filler_clip) {
+ data["use_filler_clip"] = true;
+ data["filler_clip"] = tr.filler_clip;
+ }
+ if (tr.hold_previous) {
+ data["hold_previous"] = true;
+ }
+
+ ret[keys[i]] = data;
+ }
+ return ret;
+}
+
+bool AudioStreamInteractive::has_transition(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ return transition_map.has(tk);
+}
+
+void AudioStreamInteractive::erase_transition(int p_from_clip, int p_to_clip) {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND(!transition_map.has(tk));
+ AudioDriver::get_singleton()->lock();
+ transition_map.erase(tk);
+ AudioDriver::get_singleton()->unlock();
+}
+
+PackedInt32Array AudioStreamInteractive::get_transition_list() const {
+ PackedInt32Array ret;
+
+ for (const KeyValue<TransitionKey, Transition> &K : transition_map) {
+ ret.push_back(K.key.from_clip);
+ ret.push_back(K.key.to_clip);
+ }
+ return ret;
+}
+
+void AudioStreamInteractive::add_transition(int p_from_clip, int p_to_clip, TransitionFromTime p_from_time, TransitionToTime p_to_time, FadeMode p_fade_mode, float p_fade_beats, bool p_use_filler_flip, int p_filler_clip, bool p_hold_previous) {
+ ERR_FAIL_COND(p_from_clip < CLIP_ANY || p_from_clip >= clip_count);
+ ERR_FAIL_COND(p_to_clip < CLIP_ANY || p_to_clip >= clip_count);
+ ERR_FAIL_UNSIGNED_INDEX(p_from_time, TRANSITION_FROM_TIME_MAX);
+ ERR_FAIL_UNSIGNED_INDEX(p_to_time, TRANSITION_TO_TIME_MAX);
+ ERR_FAIL_UNSIGNED_INDEX(p_fade_mode, FADE_MAX);
+
+ Transition tr;
+ tr.from_time = p_from_time;
+ tr.to_time = p_to_time;
+ tr.fade_mode = p_fade_mode;
+ tr.fade_beats = p_fade_beats;
+ tr.use_filler_clip = p_use_filler_flip;
+ tr.filler_clip = p_filler_clip;
+ tr.hold_previous = p_hold_previous;
+
+ TransitionKey tk(p_from_clip, p_to_clip);
+
+ AudioDriver::get_singleton()->lock();
+ transition_map[tk] = tr;
+ AudioDriver::get_singleton()->unlock();
+}
+
+AudioStreamInteractive::TransitionFromTime AudioStreamInteractive::get_transition_from_time(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), TRANSITION_FROM_TIME_END);
+ return transition_map[tk].from_time;
+}
+
+AudioStreamInteractive::TransitionToTime AudioStreamInteractive::get_transition_to_time(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), TRANSITION_TO_TIME_START);
+ return transition_map[tk].to_time;
+}
+
+AudioStreamInteractive::FadeMode AudioStreamInteractive::get_transition_fade_mode(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), FADE_DISABLED);
+ return transition_map[tk].fade_mode;
+}
+
+float AudioStreamInteractive::get_transition_fade_beats(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), -1);
+ return transition_map[tk].fade_beats;
+}
+
+bool AudioStreamInteractive::is_transition_using_filler_clip(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), false);
+ return transition_map[tk].use_filler_clip;
+}
+
+int AudioStreamInteractive::get_transition_filler_clip(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), -1);
+ return transition_map[tk].filler_clip;
+}
+
+bool AudioStreamInteractive::is_transition_holding_previous(int p_from_clip, int p_to_clip) const {
+ TransitionKey tk(p_from_clip, p_to_clip);
+ ERR_FAIL_COND_V(!transition_map.has(tk), false);
+ return transition_map[tk].hold_previous;
+}
+
+#ifdef TOOLS_ENABLED
+
+PackedStringArray AudioStreamInteractive::_get_linked_undo_properties(const String &p_property, const Variant &p_new_value) const {
+ PackedStringArray ret;
+
+ if (p_property.begins_with("clip_") && p_property.ends_with("/stream")) {
+ int clip = p_property.get_slicec('_', 1).to_int();
+ if (clip < clip_count) {
+ ret.push_back("clip_" + itos(clip) + "/name");
+ }
+ }
+
+ if (p_property == "clip_count") {
+ int new_clip_count = p_new_value;
+
+ if (new_clip_count < clip_count) {
+ for (int i = 0; i < clip_count; i++) {
+ if (clips[i].auto_advance_next_clip >= new_clip_count) {
+ ret.push_back("clip_" + itos(i) + "/auto_advance");
+ ret.push_back("clip_" + itos(i) + "/next_clip");
+ }
+ }
+
+ ret.push_back("_transitions");
+ if (initial_clip >= new_clip_count) {
+ ret.push_back("initial_clip");
+ }
+ }
+ }
+ return ret;
+}
+
+template <class T>
+static void _test_and_swap(T &p_elem, uint32_t p_a, uint32_t p_b) {
+ if ((uint32_t)p_elem == p_a) {
+ p_elem = p_b;
+ } else if (uint32_t(p_elem) == p_b) {
+ p_elem = p_a;
+ }
+}
+
+void AudioStreamInteractive::_inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b) {
+ ERR_FAIL_INDEX(p_item_a, (uint32_t)clip_count);
+ ERR_FAIL_UNSIGNED_INDEX(p_item_b, (uint32_t)clip_count);
+
+ for (int i = 0; i < clip_count; i++) {
+ _test_and_swap(clips[i].auto_advance_next_clip, p_item_a, p_item_b);
+ }
+
+ Vector<TransitionKey> to_remove;
+ HashMap<TransitionKey, Transition, TransitionKeyHasher> to_add;
+
+ for (KeyValue<TransitionKey, Transition> &K : transition_map) {
+ if (K.key.from_clip == p_item_a || K.key.from_clip == p_item_b || K.key.to_clip == p_item_a || K.key.to_clip == p_item_b) {
+ to_remove.push_back(K.key);
+ TransitionKey new_key = K.key;
+ _test_and_swap(new_key.from_clip, p_item_a, p_item_b);
+ _test_and_swap(new_key.to_clip, p_item_a, p_item_b);
+ to_add[new_key] = K.value;
+ }
+ }
+
+ for (int i = 0; i < to_remove.size(); i++) {
+ transition_map.erase(to_remove[i]);
+ }
+
+ for (KeyValue<TransitionKey, Transition> &K : to_add) {
+ transition_map.insert(K.key, K.value);
+ }
+
+ SWAP(clips[p_item_a], clips[p_item_b]);
+
+ stream_name_cache = "";
+
+ notify_property_list_changed();
+ emit_signal(SNAME("parameter_list_changed"));
+}
+
+String AudioStreamInteractive::_get_streams_hint() const {
+ if (!stream_name_cache.is_empty()) {
+ return stream_name_cache;
+ }
+
+ for (int i = 0; i < clip_count; i++) {
+ if (i > 0) {
+ stream_name_cache += ",";
+ }
+ String n = String(clips[i].name).replace(",", " ");
+
+ if (n == "" && clips[i].stream.is_valid()) {
+ if (!clips[i].stream->get_name().is_empty()) {
+ n = clips[i].stream->get_name().replace(",", " ");
+ } else if (clips[i].stream->get_path().is_resource_file()) {
+ n = clips[i].stream->get_path().get_file().replace(",", " ");
+ }
+ }
+
+ if (n == "") {
+ n = "Clip " + itos(i);
+ }
+
+ stream_name_cache += n;
+ }
+
+ return stream_name_cache;
+}
+
+#endif
+void AudioStreamInteractive::_validate_property(PropertyInfo &r_property) const {
+ String prop = r_property.name;
+
+#ifdef TOOLS_ENABLED
+ if (prop == "switch_to") {
+ r_property.hint_string = _get_streams_hint();
+ return;
+ }
+#endif
+
+ if (prop == "initial_clip") {
+#ifdef TOOLS_ENABLED
+ r_property.hint_string = _get_streams_hint();
+#endif
+ } else if (prop.begins_with("clip_") && prop != "clip_count") {
+ int clip = prop.get_slicec('_', 1).to_int();
+ if (clip >= clip_count) {
+ r_property.usage = PROPERTY_USAGE_INTERNAL;
+ } else if (prop == "clip_" + itos(clip) + "/next_clip") {
+ if (clips[clip].auto_advance != AUTO_ADVANCE_ENABLED) {
+ r_property.usage = 0;
+ } else {
+#ifdef TOOLS_ENABLED
+ r_property.hint_string = _get_streams_hint();
+#endif
+ }
+ }
+ }
+}
+
+void AudioStreamInteractive::get_parameter_list(List<Parameter> *r_parameters) {
+ String clip_names;
+ for (int i = 0; i < clip_count; i++) {
+ clip_names += ",";
+ clip_names += clips[i].name;
+ }
+ r_parameters->push_back(Parameter(PropertyInfo(Variant::STRING, "switch_to_clip", PROPERTY_HINT_ENUM, clip_names, PROPERTY_USAGE_EDITOR), ""));
+}
+
+void AudioStreamInteractive::_bind_methods() {
+#ifdef TOOLS_ENABLED
+ ClassDB::bind_method(D_METHOD("_get_linked_undo_properties", "for_property", "for_value"), &AudioStreamInteractive::_get_linked_undo_properties);
+ ClassDB::bind_method(D_METHOD("_inspector_array_swap_clip", "a", "b"), &AudioStreamInteractive::_inspector_array_swap_clip);
+#endif
+
+ // CLIPS
+
+ ClassDB::bind_method(D_METHOD("set_clip_count", "clip_count"), &AudioStreamInteractive::set_clip_count);
+ ClassDB::bind_method(D_METHOD("get_clip_count"), &AudioStreamInteractive::get_clip_count);
+
+ ClassDB::bind_method(D_METHOD("set_initial_clip", "clip_index"), &AudioStreamInteractive::set_initial_clip);
+ ClassDB::bind_method(D_METHOD("get_initial_clip"), &AudioStreamInteractive::get_initial_clip);
+
+ ClassDB::bind_method(D_METHOD("set_clip_name", "clip_index", "name"), &AudioStreamInteractive::set_clip_name);
+ ClassDB::bind_method(D_METHOD("get_clip_name", "clip_index"), &AudioStreamInteractive::get_clip_name);
+
+ ClassDB::bind_method(D_METHOD("set_clip_stream", "clip_index", "stream"), &AudioStreamInteractive::set_clip_stream);
+ ClassDB::bind_method(D_METHOD("get_clip_stream", "clip_index"), &AudioStreamInteractive::get_clip_stream);
+
+ ClassDB::bind_method(D_METHOD("set_clip_auto_advance", "clip_index", "mode"), &AudioStreamInteractive::set_clip_auto_advance);
+ ClassDB::bind_method(D_METHOD("get_clip_auto_advance", "clip_index"), &AudioStreamInteractive::get_clip_auto_advance);
+
+ ClassDB::bind_method(D_METHOD("set_clip_auto_advance_next_clip", "clip_index", "auto_advance_next_clip"), &AudioStreamInteractive::set_clip_auto_advance_next_clip);
+ ClassDB::bind_method(D_METHOD("get_clip_auto_advance_next_clip", "clip_index"), &AudioStreamInteractive::get_clip_auto_advance_next_clip);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "initial_clip", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT), "set_initial_clip", "get_initial_clip");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_count", PROPERTY_HINT_RANGE, "1," + itos(MAX_CLIPS), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Clips,clip_,page_size=999,unfoldable,numbered,swap_method=_inspector_array_swap_clip,add_button_text=" + String(RTR("Add Clip"))), "set_clip_count", "get_clip_count");
+ for (int i = 0; i < MAX_CLIPS; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::STRING_NAME, "clip_" + itos(i) + "/name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_name", "get_clip_name", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "clip_" + itos(i) + "/stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_stream", "get_clip_stream", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "clip_" + itos(i) + "/auto_advance", PROPERTY_HINT_ENUM, "Disabled,Enabled,ReturnToHold", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_auto_advance", "get_clip_auto_advance", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::INT, "clip_" + itos(i) + "/next_clip", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_clip_auto_advance_next_clip", "get_clip_auto_advance_next_clip", i);
+ }
+
+ // TRANSITIONS
+
+ ClassDB::bind_method(D_METHOD("add_transition", "from_clip", "to_clip", "from_time", "to_time", "fade_mode", "fade_beats", "use_filler_clip", "filler_clip", "hold_previous"), &AudioStreamInteractive::add_transition, DEFVAL(false), DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("has_transition", "from_clip", "to_clip"), &AudioStreamInteractive::has_transition);
+ ClassDB::bind_method(D_METHOD("erase_transition", "from_clip", "to_clip"), &AudioStreamInteractive::erase_transition);
+ ClassDB::bind_method(D_METHOD("get_transition_list"), &AudioStreamInteractive::get_transition_list);
+
+ ClassDB::bind_method(D_METHOD("get_transition_from_time", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_from_time);
+ ClassDB::bind_method(D_METHOD("get_transition_to_time", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_to_time);
+ ClassDB::bind_method(D_METHOD("get_transition_fade_mode", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_fade_mode);
+ ClassDB::bind_method(D_METHOD("get_transition_fade_beats", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_fade_beats);
+ ClassDB::bind_method(D_METHOD("is_transition_using_filler_clip", "from_clip", "to_clip"), &AudioStreamInteractive::is_transition_using_filler_clip);
+ ClassDB::bind_method(D_METHOD("get_transition_filler_clip", "from_clip", "to_clip"), &AudioStreamInteractive::get_transition_filler_clip);
+ ClassDB::bind_method(D_METHOD("is_transition_holding_previous", "from_clip", "to_clip"), &AudioStreamInteractive::is_transition_holding_previous);
+
+ ClassDB::bind_method(D_METHOD("_set_transitions", "transitions"), &AudioStreamInteractive::_set_transitions);
+ ClassDB::bind_method(D_METHOD("_get_transitions"), &AudioStreamInteractive::_get_transitions);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_transitions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_transitions", "_get_transitions");
+
+ BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_IMMEDIATE);
+ BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_NEXT_BEAT);
+ BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_NEXT_BAR);
+ BIND_ENUM_CONSTANT(TRANSITION_FROM_TIME_END);
+
+ BIND_ENUM_CONSTANT(TRANSITION_TO_TIME_SAME_POSITION);
+ BIND_ENUM_CONSTANT(TRANSITION_TO_TIME_START);
+
+ BIND_ENUM_CONSTANT(FADE_DISABLED);
+ BIND_ENUM_CONSTANT(FADE_IN);
+ BIND_ENUM_CONSTANT(FADE_OUT);
+ BIND_ENUM_CONSTANT(FADE_CROSS);
+ BIND_ENUM_CONSTANT(FADE_AUTOMATIC);
+
+ BIND_ENUM_CONSTANT(AUTO_ADVANCE_DISABLED);
+ BIND_ENUM_CONSTANT(AUTO_ADVANCE_ENABLED);
+ BIND_ENUM_CONSTANT(AUTO_ADVANCE_RETURN_TO_HOLD);
+
+ BIND_CONSTANT(CLIP_ANY);
+}
+
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+AudioStreamPlaybackInteractive::AudioStreamPlaybackInteractive() {
+}
+
+AudioStreamPlaybackInteractive::~AudioStreamPlaybackInteractive() {
+}
+
+void AudioStreamPlaybackInteractive::stop() {
+ if (!active) {
+ return;
+ }
+
+ active = false;
+
+ for (int i = 0; i < AudioStreamInteractive::MAX_CLIPS; i++) {
+ if (states[i].playback.is_valid()) {
+ states[i].playback->stop();
+ }
+ states[i].fade_speed = 0.0;
+ states[i].fade_volume = 0.0;
+ states[i].fade_wait = 0.0;
+ states[i].reset_fade();
+ states[i].active = false;
+ states[i].auto_advance = -1;
+ states[i].first_mix = true;
+ }
+}
+
+void AudioStreamPlaybackInteractive::start(double p_from_pos) {
+ if (active) {
+ stop();
+ }
+
+ if (version != stream->version) {
+ for (int i = 0; i < AudioStreamInteractive::MAX_CLIPS; i++) {
+ Ref<AudioStream> src_stream;
+ if (i < stream->clip_count) {
+ src_stream = stream->clips[i].stream;
+ }
+ if (states[i].stream != src_stream) {
+ states[i].stream.unref();
+ states[i].playback.unref();
+
+ states[i].stream = src_stream;
+ states[i].playback = src_stream->instantiate_playback();
+ }
+ }
+
+ version = stream->version;
+ }
+
+ int current = stream->initial_clip;
+ if (current < 0 || current >= stream->clip_count) {
+ return; // No playback possible.
+ }
+ if (!states[current].playback.is_valid()) {
+ return; //no playback possible
+ }
+ active = true;
+
+ _queue(current, false);
+}
+
+void AudioStreamPlaybackInteractive::_queue(int p_to_clip_index, bool p_is_auto_advance) {
+ ERR_FAIL_INDEX(p_to_clip_index, stream->clip_count);
+ ERR_FAIL_COND(states[p_to_clip_index].playback.is_null());
+
+ if (playback_current == -1) {
+ // Nothing to do, start.
+ int current = p_to_clip_index;
+ State &state = states[current];
+ state.active = true;
+ state.fade_wait = 0;
+ state.fade_volume = 1.0;
+ state.fade_speed = 0;
+ state.first_mix = true;
+
+ state.playback->start(0);
+
+ playback_current = current;
+
+ if (stream->clips[current].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_ENABLED && stream->clips[current].auto_advance_next_clip >= 0 && stream->clips[current].auto_advance_next_clip < stream->clip_count && stream->clips[current].auto_advance_next_clip != current) {
+ //prepare auto advance
+ state.auto_advance = stream->clips[current].auto_advance_next_clip;
+ }
+ return;
+ }
+
+ for (int i = 0; i < stream->clip_count; i++) {
+ if (i == playback_current || i == p_to_clip_index) {
+ continue;
+ }
+ if (states[i].active && states[i].fade_wait > 0) { // Waiting to kick in, terminate because change of plans.
+ states[i].playback->stop();
+ states[i].reset_fade();
+ states[i].active = false;
+ }
+ }
+
+ State &from_state = states[playback_current];
+ State &to_state = states[p_to_clip_index];
+
+ AudioStreamInteractive::Transition transition; // Use an empty transition by default
+
+ AudioStreamInteractive::TransitionKey tkeys[4] = {
+ AudioStreamInteractive::TransitionKey(playback_current, p_to_clip_index),
+ AudioStreamInteractive::TransitionKey(playback_current, AudioStreamInteractive::CLIP_ANY),
+ AudioStreamInteractive::TransitionKey(AudioStreamInteractive::CLIP_ANY, p_to_clip_index),
+ AudioStreamInteractive::TransitionKey(AudioStreamInteractive::CLIP_ANY, AudioStreamInteractive::CLIP_ANY)
+ };
+
+ for (int i = 0; i < 4; i++) {
+ if (stream->transition_map.has(tkeys[i])) {
+ transition = stream->transition_map[tkeys[i]];
+ break;
+ }
+ }
+
+ if (transition.fade_mode == AudioStreamInteractive::FADE_AUTOMATIC) {
+ // Adjust automatic mode based on context.
+ if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_START) {
+ transition.fade_mode = AudioStreamInteractive::FADE_OUT;
+ } else {
+ transition.fade_mode = AudioStreamInteractive::FADE_CROSS;
+ }
+ }
+
+ if (p_is_auto_advance) {
+ transition.from_time = AudioStreamInteractive::TRANSITION_FROM_TIME_END;
+ if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION) {
+ transition.to_time = AudioStreamInteractive::TRANSITION_TO_TIME_START;
+ }
+ }
+
+ // Prepare the fadeout
+ float current_pos = from_state.playback->get_playback_position();
+
+ float src_fade_wait = 0;
+ float dst_seek_to = 0;
+ float fade_speed = 0;
+ bool src_no_loop = false;
+
+ if (from_state.stream->get_bpm()) {
+ // Check if source speed has BPM, if so, transition syncs to BPM
+ float beat_sec = 60 / float(from_state.stream->get_bpm());
+ switch (transition.from_time) {
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_IMMEDIATE: {
+ src_fade_wait = 0;
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BEAT: {
+ float remainder = Math::fmod(current_pos, beat_sec);
+ src_fade_wait = beat_sec - remainder;
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BAR: {
+ float bar_sec = beat_sec * from_state.stream->get_bar_beats();
+ float remainder = Math::fmod(current_pos, bar_sec);
+ src_fade_wait = bar_sec - remainder;
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_END: {
+ float end = from_state.stream->get_beat_count() > 0 ? float(from_state.stream->get_beat_count() * beat_sec) : from_state.stream->get_length();
+ if (end == 0) {
+ // Stream does not have a length.
+ src_fade_wait = 0;
+ } else {
+ src_fade_wait = end - current_pos;
+ }
+
+ if (!from_state.stream->has_loop()) {
+ src_no_loop = true;
+ }
+
+ } break;
+ default: {
+ }
+ }
+ // Fade speed also aligned to BPM
+ fade_speed = 1.0 / (transition.fade_beats * beat_sec);
+ } else {
+ // Source has no BPM, so just simple transition.
+ if (transition.from_time == AudioStreamInteractive::TRANSITION_FROM_TIME_END && from_state.stream->get_length() > 0) {
+ float end = from_state.stream->get_length();
+ src_fade_wait = end - current_pos;
+ if (!from_state.stream->has_loop()) {
+ src_no_loop = true;
+ }
+ } else {
+ src_fade_wait = 0;
+ }
+ fade_speed = 1.0 / transition.fade_beats;
+ }
+
+ if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_PREVIOUS_POSITION && to_state.stream->get_length() > 0.0) {
+ dst_seek_to = to_state.previous_position;
+ } else if (transition.to_time == AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION && transition.from_time != AudioStreamInteractive::TRANSITION_FROM_TIME_END && to_state.stream->get_length() > 0.0) {
+ // Seeking to basically same position as when we start fading.
+ dst_seek_to = current_pos + src_fade_wait;
+ float end;
+ if (to_state.stream->get_bpm() > 0 && to_state.stream->get_beat_count()) {
+ float beat_sec = 60 / float(to_state.stream->get_bpm());
+ end = to_state.stream->get_beat_count() * beat_sec;
+ } else {
+ end = to_state.stream->get_length();
+ }
+
+ if (dst_seek_to > end) {
+ // Seeking too far away.
+ dst_seek_to = 0; //past end, loop to beginning.
+ }
+
+ } else {
+ // Seek to Start
+ dst_seek_to = 0.0;
+ }
+
+ if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_IN) {
+ if (src_no_loop) {
+ // If there is no fade in the source stream, then let it continue until it ends.
+ from_state.fade_wait = 0;
+ from_state.fade_speed = 0;
+ } else {
+ // Otherwise force a very quick fade to avoid clicks
+ from_state.fade_wait = src_fade_wait;
+ from_state.fade_speed = 1.0 / -0.001;
+ }
+ } else {
+ // Regular fade.
+ from_state.fade_wait = src_fade_wait;
+ from_state.fade_speed = -fade_speed;
+ }
+ // keep volume, since it may have been fading in from something else.
+
+ to_state.playback->start(dst_seek_to);
+ to_state.active = true;
+ to_state.fade_volume = 0.0;
+ to_state.first_mix = true;
+
+ int auto_advance_to = -1;
+
+ if (stream->clips[p_to_clip_index].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_ENABLED) {
+ int next_clip = stream->clips[p_to_clip_index].auto_advance_next_clip;
+ if (next_clip >= 0 && next_clip < (int)stream->clip_count && states[next_clip].playback.is_valid() && next_clip != p_to_clip_index && next_clip != playback_current && (!transition.use_filler_clip || next_clip != transition.filler_clip)) {
+ auto_advance_to = next_clip;
+ }
+ }
+
+ if (return_memory != -1 && stream->clips[p_to_clip_index].auto_advance == AudioStreamInteractive::AUTO_ADVANCE_RETURN_TO_HOLD) {
+ auto_advance_to = return_memory;
+ return_memory = -1;
+ }
+
+ if (transition.hold_previous) {
+ return_memory = playback_current;
+ }
+
+ if (transition.use_filler_clip && transition.filler_clip >= 0 && transition.filler_clip < (int)stream->clip_count && states[transition.filler_clip].playback.is_valid() && playback_current != transition.filler_clip && p_to_clip_index != transition.filler_clip) {
+ State &filler_state = states[transition.filler_clip];
+
+ filler_state.playback->start(0);
+ filler_state.active = true;
+
+ // Filler state does not fade (bake fade in the audio clip if you want fading.
+ filler_state.fade_volume = 1.0;
+ filler_state.fade_speed = 0.0;
+
+ filler_state.fade_wait = src_fade_wait;
+ filler_state.first_mix = true;
+
+ float filler_end;
+ if (filler_state.stream->get_bpm() > 0 && filler_state.stream->get_beat_count() > 0) {
+ float filler_beat_sec = 60 / float(filler_state.stream->get_bpm());
+ filler_end = filler_beat_sec * filler_state.stream->get_beat_count();
+ } else {
+ filler_end = filler_state.stream->get_length();
+ }
+
+ if (!filler_state.stream->has_loop()) {
+ src_no_loop = true;
+ }
+
+ if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_OUT) {
+ // No fading, immediately start at full volume.
+ to_state.fade_volume = 0.0;
+ to_state.fade_speed = 1.0; //start at full volume, as filler is meant as a transition.
+ } else {
+ // Fade enable, prepare fade.
+ to_state.fade_volume = 0.0;
+ to_state.fade_speed = fade_speed;
+ }
+
+ to_state.fade_wait = src_fade_wait + filler_end;
+
+ } else {
+ to_state.fade_wait = src_fade_wait;
+
+ if (transition.fade_mode == AudioStreamInteractive::FADE_DISABLED || transition.fade_mode == AudioStreamInteractive::FADE_OUT) {
+ to_state.fade_volume = 1.0;
+ to_state.fade_speed = 0.0;
+ } else {
+ to_state.fade_volume = 0.0;
+ to_state.fade_speed = fade_speed;
+ }
+
+ to_state.auto_advance = auto_advance_to;
+ }
+}
+
+void AudioStreamPlaybackInteractive::seek(double p_time) {
+ // Seek not supported
+}
+
+int AudioStreamPlaybackInteractive::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+ if (active && version != stream->version) {
+ stop();
+ }
+
+ if (switch_request != -1) {
+ _queue(switch_request, false);
+ switch_request = -1;
+ }
+
+ if (!active) {
+ for (int i = 0; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0.0, 0.0);
+ }
+ return p_frames;
+ }
+
+ int todo = p_frames;
+
+ while (todo) {
+ int to_mix = MIN(todo, BUFFER_SIZE);
+ _mix_internal(to_mix);
+ for (int i = 0; i < to_mix; i++) {
+ p_buffer[i] = mix_buffer[i];
+ }
+ p_buffer += to_mix;
+ todo -= to_mix;
+ }
+
+ return p_frames;
+}
+
+void AudioStreamPlaybackInteractive::_mix_internal(int p_frames) {
+ for (int i = 0; i < p_frames; i++) {
+ mix_buffer[i] = AudioFrame(0, 0);
+ }
+
+ for (int i = 0; i < stream->clip_count; i++) {
+ if (!states[i].active) {
+ continue;
+ }
+
+ _mix_internal_state(i, p_frames);
+ }
+}
+
+void AudioStreamPlaybackInteractive::_mix_internal_state(int p_state_idx, int p_frames) {
+ State &state = states[p_state_idx];
+ double mix_rate = double(AudioServer::get_singleton()->get_mix_rate());
+ double frame_inc = 1.0 / mix_rate;
+
+ int from_frame = 0;
+ int queue_next = -1;
+
+ if (state.first_mix) {
+ // Did not start mixing yet, wait.
+ double mix_time = p_frames * frame_inc;
+ if (state.fade_wait < mix_time) {
+ // time to start!
+ from_frame = state.fade_wait * mix_rate;
+ state.fade_wait = 0;
+ queue_next = state.auto_advance;
+ playback_current = p_state_idx;
+ state.first_mix = false;
+ } else {
+ // This is for fade in of new stream.
+ state.fade_wait -= mix_time;
+ return; // Nothing to do
+ }
+ }
+
+ state.previous_position = state.playback->get_playback_position();
+ state.playback->mix(temp_buffer + from_frame, 1.0, p_frames - from_frame);
+
+ double frame_fade_inc = state.fade_speed * frame_inc;
+
+ for (int i = from_frame; i < p_frames; i++) {
+ if (state.fade_wait) {
+ // This is for fade out of existing stream;
+ state.fade_wait -= frame_inc;
+ if (state.fade_wait < 0.0) {
+ state.fade_wait = 0.0;
+ }
+ } else if (frame_fade_inc > 0) {
+ state.fade_volume += frame_fade_inc;
+ if (state.fade_volume >= 1.0) {
+ state.fade_speed = 0.0;
+ frame_fade_inc = 0.0;
+ state.fade_volume = 1.0;
+ }
+ } else if (frame_fade_inc < 0.0) {
+ state.fade_volume += frame_fade_inc;
+ if (state.fade_volume <= 0.0) {
+ state.fade_speed = 0.0;
+ frame_fade_inc = 0.0;
+ state.fade_volume = 0.0;
+ state.playback->stop(); // Stop playback and break, no point to continue mixing
+ break;
+ }
+ }
+
+ mix_buffer[i] += temp_buffer[i] * state.fade_volume;
+ state.previous_position += frame_inc;
+ }
+
+ if (!state.playback->is_playing()) {
+ // It finished because it either reached end or faded out, so deactivate and continue.
+ state.active = false;
+ }
+ if (queue_next != -1) {
+ _queue(queue_next, true);
+ }
+}
+
+void AudioStreamPlaybackInteractive::tag_used_streams() {
+ for (int i = 0; i < stream->clip_count; i++) {
+ if (states[i].active && !states[i].first_mix && states[i].playback->is_playing()) {
+ states[i].stream->tag_used(states[i].playback->get_playback_position());
+ }
+ }
+ stream->tag_used(0);
+}
+
+void AudioStreamPlaybackInteractive::switch_to_clip_by_name(const StringName &p_name) {
+ if (p_name == StringName()) {
+ switch_request = -1;
+ return;
+ }
+
+ for (int i = 0; i < stream->get_clip_count(); i++) {
+ if (stream->get_clip_name(i) == p_name) {
+ switch_request = i;
+ return;
+ }
+ }
+ ERR_FAIL_MSG("Clip not found: " + String(p_name));
+}
+
+void AudioStreamPlaybackInteractive::set_parameter(const StringName &p_name, const Variant &p_value) {
+ if (p_name == SNAME("switch_to_clip")) {
+ switch_to_clip_by_name(p_value);
+ }
+}
+
+Variant AudioStreamPlaybackInteractive::get_parameter(const StringName &p_name) const {
+ if (p_name == SNAME("switch_to_clip")) {
+ for (int i = 0; i < stream->get_clip_count(); i++) {
+ if (switch_request != -1) {
+ if (switch_request == i) {
+ return String(stream->get_clip_name(i));
+ }
+ } else if (playback_current == i) {
+ return String(stream->get_clip_name(i));
+ }
+ }
+ return "";
+ }
+
+ return Variant();
+}
+
+void AudioStreamPlaybackInteractive::switch_to_clip(int p_index) {
+ switch_request = p_index;
+}
+
+int AudioStreamPlaybackInteractive::get_loop_count() const {
+ return 0; // Looping not supported
+}
+
+double AudioStreamPlaybackInteractive::get_playback_position() const {
+ return 0.0;
+}
+
+bool AudioStreamPlaybackInteractive::is_playing() const {
+ return active;
+}
+
+void AudioStreamPlaybackInteractive::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("switch_to_clip_by_name", "clip_name"), &AudioStreamPlaybackInteractive::switch_to_clip_by_name);
+ ClassDB::bind_method(D_METHOD("switch_to_clip", "clip_index"), &AudioStreamPlaybackInteractive::switch_to_clip);
+}
diff --git a/modules/interactive_music/audio_stream_interactive.h b/modules/interactive_music/audio_stream_interactive.h
new file mode 100644
index 0000000000..12d3ce8aad
--- /dev/null
+++ b/modules/interactive_music/audio_stream_interactive.h
@@ -0,0 +1,270 @@
+/**************************************************************************/
+/* audio_stream_interactive.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef AUDIO_STREAM_INTERACTIVE_H
+#define AUDIO_STREAM_INTERACTIVE_H
+
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamPlaybackInteractive;
+
+class AudioStreamInteractive : public AudioStream {
+ GDCLASS(AudioStreamInteractive, AudioStream)
+ OBJ_SAVE_TYPE(AudioStream)
+public:
+ enum TransitionFromTime {
+ TRANSITION_FROM_TIME_IMMEDIATE,
+ TRANSITION_FROM_TIME_NEXT_BEAT,
+ TRANSITION_FROM_TIME_NEXT_BAR,
+ TRANSITION_FROM_TIME_END,
+ TRANSITION_FROM_TIME_MAX
+ };
+
+ enum TransitionToTime {
+ TRANSITION_TO_TIME_SAME_POSITION,
+ TRANSITION_TO_TIME_START,
+ TRANSITION_TO_TIME_PREVIOUS_POSITION,
+ TRANSITION_TO_TIME_MAX,
+ };
+
+ enum FadeMode {
+ FADE_DISABLED,
+ FADE_IN,
+ FADE_OUT,
+ FADE_CROSS,
+ FADE_AUTOMATIC,
+ FADE_MAX
+ };
+
+ enum AutoAdvanceMode {
+ AUTO_ADVANCE_DISABLED,
+ AUTO_ADVANCE_ENABLED,
+ AUTO_ADVANCE_RETURN_TO_HOLD,
+ };
+
+ enum {
+ CLIP_ANY = -1
+ };
+
+private:
+ friend class AudioStreamPlaybackInteractive;
+ int sample_rate = 44100;
+ bool stereo = true;
+ int initial_clip = 0;
+
+ double time = 0;
+
+ enum {
+ MAX_CLIPS = 63, // Because we use bitmasks for transition matching.
+ MAX_TRANSITIONS = 63,
+ };
+
+ struct Clip {
+ StringName name;
+ Ref<AudioStream> stream;
+
+ AutoAdvanceMode auto_advance = AUTO_ADVANCE_DISABLED;
+ int auto_advance_next_clip = 0;
+ };
+
+ Clip clips[MAX_CLIPS];
+
+ struct Transition {
+ TransitionFromTime from_time = TRANSITION_FROM_TIME_NEXT_BEAT;
+ TransitionToTime to_time = TRANSITION_TO_TIME_START;
+ FadeMode fade_mode = FADE_AUTOMATIC;
+ int fade_beats = 1;
+ bool use_filler_clip = false;
+ int filler_clip = 0;
+ bool hold_previous = false;
+ };
+
+ struct TransitionKey {
+ uint32_t from_clip;
+ uint32_t to_clip;
+ bool operator==(const TransitionKey &p_key) const {
+ return from_clip == p_key.from_clip && to_clip == p_key.to_clip;
+ }
+ TransitionKey(uint32_t p_from_clip = 0, uint32_t p_to_clip = 0) {
+ from_clip = p_from_clip;
+ to_clip = p_to_clip;
+ }
+ };
+
+ struct TransitionKeyHasher {
+ static _FORCE_INLINE_ uint32_t hash(const TransitionKey &p_key) {
+ uint32_t h = hash_murmur3_one_32(p_key.from_clip);
+ return hash_murmur3_one_32(p_key.to_clip, h);
+ }
+ };
+
+ HashMap<TransitionKey, Transition, TransitionKeyHasher> transition_map;
+
+ uint64_t version = 1; // Used to stop playback instances for incompatibility.
+ int clip_count = 0;
+
+ HashSet<AudioStreamPlaybackInteractive *> playbacks;
+
+#ifdef TOOLS_ENABLED
+
+ mutable String stream_name_cache;
+ String _get_streams_hint() const;
+ PackedStringArray _get_linked_undo_properties(const String &p_property, const Variant &p_new_value) const;
+ void _inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b);
+
+#endif
+
+ void _set_transitions(const Dictionary &p_transitions);
+ Dictionary _get_transitions() const;
+
+public:
+ // CLIPS
+ void set_clip_count(int p_count);
+ int get_clip_count() const;
+
+ void set_initial_clip(int p_clip);
+ int get_initial_clip() const;
+
+ void set_clip_name(int p_clip, const StringName &p_name);
+ StringName get_clip_name(int p_clip) const;
+
+ void set_clip_stream(int p_clip, const Ref<AudioStream> &p_stream);
+ Ref<AudioStream> get_clip_stream(int p_clip) const;
+
+ void set_clip_auto_advance(int p_clip, AutoAdvanceMode p_mode);
+ AutoAdvanceMode get_clip_auto_advance(int p_clip) const;
+
+ void set_clip_auto_advance_next_clip(int p_clip, int p_index);
+ int get_clip_auto_advance_next_clip(int p_clip) const;
+
+ // TRANSITIONS
+
+ void add_transition(int p_from_clip, int p_to_clip, TransitionFromTime p_from_time, TransitionToTime p_to_time, FadeMode p_fade_mode, float p_fade_beats, bool p_use_filler_flip = false, int p_filler_clip = -1, bool p_hold_previous = false);
+ TransitionFromTime get_transition_from_time(int p_from_clip, int p_to_clip) const;
+ TransitionToTime get_transition_to_time(int p_from_clip, int p_to_clip) const;
+ FadeMode get_transition_fade_mode(int p_from_clip, int p_to_clip) const;
+ float get_transition_fade_beats(int p_from_clip, int p_to_clip) const;
+ bool is_transition_using_filler_clip(int p_from_clip, int p_to_clip) const;
+ int get_transition_filler_clip(int p_from_clip, int p_to_clip) const;
+ bool is_transition_holding_previous(int p_from_clip, int p_to_clip) const;
+
+ bool has_transition(int p_from_clip, int p_to_clip) const;
+ void erase_transition(int p_from_clip, int p_to_clip);
+
+ PackedInt32Array get_transition_list() const;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
+ virtual String get_stream_name() const override;
+ virtual double get_length() const override { return 0; }
+ AudioStreamInteractive();
+
+protected:
+ virtual void get_parameter_list(List<Parameter> *r_parameters) override;
+
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &r_property) const;
+};
+
+VARIANT_ENUM_CAST(AudioStreamInteractive::TransitionFromTime)
+VARIANT_ENUM_CAST(AudioStreamInteractive::TransitionToTime)
+VARIANT_ENUM_CAST(AudioStreamInteractive::AutoAdvanceMode)
+VARIANT_ENUM_CAST(AudioStreamInteractive::FadeMode)
+
+class AudioStreamPlaybackInteractive : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackInteractive, AudioStreamPlayback)
+ friend class AudioStreamInteractive;
+
+private:
+ Ref<AudioStreamInteractive> stream;
+ uint64_t version = 0;
+
+ enum {
+ BUFFER_SIZE = 1024
+ };
+
+ AudioFrame mix_buffer[BUFFER_SIZE];
+ AudioFrame temp_buffer[BUFFER_SIZE];
+
+ struct State {
+ Ref<AudioStream> stream;
+ Ref<AudioStreamPlayback> playback;
+ bool active = false;
+ double fade_wait = 0; // Time to wait until fade kicks-in
+ double fade_volume = 1.0;
+ double fade_speed = 0; // Fade speed, negative or positive
+ int auto_advance = -1;
+ bool first_mix = true;
+ double previous_position = 0;
+
+ void reset_fade() {
+ fade_wait = 0;
+ fade_volume = 1.0;
+ fade_speed = 0;
+ }
+ };
+
+ State states[AudioStreamInteractive::MAX_CLIPS];
+ int playback_current = -1;
+
+ bool active = false;
+ int return_memory = -1;
+
+ void _mix_internal(int p_frames);
+ void _mix_internal_state(int p_state_idx, int p_frames);
+
+ void _queue(int p_to_clip_index, bool p_is_auto_advance);
+
+ int switch_request = -1;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void start(double p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+ virtual int get_loop_count() const override; // times it looped
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+
+ virtual void tag_used_streams() override;
+
+ void switch_to_clip_by_name(const StringName &p_name);
+ void switch_to_clip(int p_index);
+
+ virtual void set_parameter(const StringName &p_name, const Variant &p_value) override;
+ virtual Variant get_parameter(const StringName &p_name) const override;
+
+ AudioStreamPlaybackInteractive();
+ ~AudioStreamPlaybackInteractive();
+};
+
+#endif // AUDIO_STREAM_INTERACTIVE_H
diff --git a/modules/interactive_music/audio_stream_playlist.cpp b/modules/interactive_music/audio_stream_playlist.cpp
new file mode 100644
index 0000000000..f47035b30c
--- /dev/null
+++ b/modules/interactive_music/audio_stream_playlist.cpp
@@ -0,0 +1,406 @@
+/**************************************************************************/
+/* audio_stream_playlist.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "audio_stream_playlist.h"
+
+#include "core/math/math_funcs.h"
+#include "core/string/print_string.h"
+
+Ref<AudioStreamPlayback> AudioStreamPlaylist::instantiate_playback() {
+ Ref<AudioStreamPlaybackPlaylist> playback_playlist;
+ playback_playlist.instantiate();
+ playback_playlist->playlist = Ref<AudioStreamPlaylist>(this);
+ playback_playlist->_update_playback_instances();
+ playbacks.insert(playback_playlist.operator->());
+ return playback_playlist;
+}
+
+String AudioStreamPlaylist::get_stream_name() const {
+ return "Playlist";
+}
+
+void AudioStreamPlaylist::set_list_stream(int p_stream_index, Ref<AudioStream> p_stream) {
+ ERR_FAIL_COND(p_stream == this);
+ ERR_FAIL_INDEX(p_stream_index, MAX_STREAMS);
+
+ AudioServer::get_singleton()->lock();
+ audio_streams[p_stream_index] = p_stream;
+ for (AudioStreamPlaybackPlaylist *E : playbacks) {
+ E->_update_playback_instances();
+ }
+ AudioServer::get_singleton()->unlock();
+}
+
+Ref<AudioStream> AudioStreamPlaylist::get_list_stream(int p_stream_index) const {
+ ERR_FAIL_INDEX_V(p_stream_index, MAX_STREAMS, Ref<AudioStream>());
+
+ return audio_streams[p_stream_index];
+}
+
+double AudioStreamPlaylist::get_bpm() const {
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ double bpm = audio_streams[i]->get_bpm();
+ if (bpm != 0.0) {
+ return bpm;
+ }
+ }
+ }
+ return 0.0;
+}
+
+double AudioStreamPlaylist::get_length() const {
+ double total_length = 0.0;
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ double bpm = audio_streams[i]->get_bpm();
+ int beat_count = audio_streams[i]->get_beat_count();
+ if (bpm > 0.0 && beat_count > 0) {
+ total_length += beat_count * 60.0 / bpm;
+ } else {
+ total_length += audio_streams[i]->get_length();
+ }
+ }
+ }
+ return total_length;
+}
+
+void AudioStreamPlaylist::set_stream_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0 || p_count > MAX_STREAMS);
+ AudioServer::get_singleton()->lock();
+ stream_count = p_count;
+ AudioServer::get_singleton()->unlock();
+ notify_property_list_changed();
+}
+
+int AudioStreamPlaylist::get_stream_count() const {
+ return stream_count;
+}
+
+void AudioStreamPlaylist::set_fade_time(float p_time) {
+ fade_time = p_time;
+}
+
+float AudioStreamPlaylist::get_fade_time() const {
+ return fade_time;
+}
+
+void AudioStreamPlaylist::set_shuffle(bool p_shuffle) {
+ shuffle = p_shuffle;
+}
+
+bool AudioStreamPlaylist::get_shuffle() const {
+ return shuffle;
+}
+
+void AudioStreamPlaylist::set_loop(bool p_loop) {
+ loop = p_loop;
+}
+
+bool AudioStreamPlaylist::has_loop() const {
+ return loop;
+}
+
+void AudioStreamPlaylist::_validate_property(PropertyInfo &r_property) const {
+ String prop = r_property.name;
+ if (prop != "stream_count" && prop.begins_with("stream_")) {
+ int stream = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ if (stream >= stream_count) {
+ r_property.usage = PROPERTY_USAGE_INTERNAL;
+ }
+ }
+}
+
+void AudioStreamPlaylist::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_stream_count", "stream_count"), &AudioStreamPlaylist::set_stream_count);
+ ClassDB::bind_method(D_METHOD("get_stream_count"), &AudioStreamPlaylist::get_stream_count);
+
+ ClassDB::bind_method(D_METHOD("get_bpm"), &AudioStreamPlaylist::get_bpm);
+
+ ClassDB::bind_method(D_METHOD("set_list_stream", "stream_index", "audio_stream"), &AudioStreamPlaylist::set_list_stream);
+ ClassDB::bind_method(D_METHOD("get_list_stream", "stream_index"), &AudioStreamPlaylist::get_list_stream);
+
+ ClassDB::bind_method(D_METHOD("set_shuffle", "shuffle"), &AudioStreamPlaylist::set_shuffle);
+ ClassDB::bind_method(D_METHOD("get_shuffle"), &AudioStreamPlaylist::get_shuffle);
+
+ ClassDB::bind_method(D_METHOD("set_fade_time", "dec"), &AudioStreamPlaylist::set_fade_time);
+ ClassDB::bind_method(D_METHOD("get_fade_time"), &AudioStreamPlaylist::get_fade_time);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "loop"), &AudioStreamPlaylist::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamPlaylist::has_loop);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shuffle"), "set_shuffle", "get_shuffle");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fade_time", PROPERTY_HINT_RANGE, "0,1,0.01,suffix:s"), "set_fade_time", "get_fade_time");
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stream_count", PROPERTY_HINT_RANGE, "0," + itos(MAX_STREAMS), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Streams,stream_,unfoldable,page_size=999,add_button_text=" + String(RTR("Add Stream"))), "set_stream_count", "get_stream_count");
+
+ for (int i = 0; i < MAX_STREAMS; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "stream_" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "AudioStream", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_list_stream", "get_list_stream", i);
+ }
+
+ BIND_CONSTANT(MAX_STREAMS);
+}
+
+//////////////////////
+//////////////////////
+
+AudioStreamPlaybackPlaylist::~AudioStreamPlaybackPlaylist() {
+ if (playlist.is_valid()) {
+ playlist->playbacks.erase(this);
+ }
+}
+
+void AudioStreamPlaybackPlaylist::stop() {
+ active = false;
+ for (int i = 0; i < playlist->stream_count; i++) {
+ if (playback[i].is_valid()) {
+ playback[i]->stop();
+ }
+ }
+}
+
+void AudioStreamPlaybackPlaylist::_update_order() {
+ for (int i = 0; i < playlist->stream_count; i++) {
+ play_order[i] = i;
+ }
+
+ if (playlist->shuffle) {
+ for (int i = 0; i < playlist->stream_count; i++) {
+ int swap_with = Math::rand() % uint32_t(playlist->stream_count);
+ SWAP(play_order[i], play_order[swap_with]);
+ }
+ }
+}
+
+void AudioStreamPlaybackPlaylist::start(double p_from_pos) {
+ if (active) {
+ stop();
+ }
+
+ p_from_pos = MAX(0, p_from_pos);
+
+ float pl_length = playlist->get_length();
+ if (p_from_pos >= pl_length) {
+ if (!playlist->loop) {
+ return; // No loop, end.
+ }
+ p_from_pos = Math::fmod((float)p_from_pos, (float)pl_length);
+ }
+
+ _update_order();
+
+ play_index = -1;
+
+ double play_ofs = p_from_pos;
+ for (int i = 0; i < playlist->stream_count; i++) {
+ int idx = play_order[i];
+ if (playlist->audio_streams[idx].is_valid()) {
+ double bpm = playlist->audio_streams[idx]->get_bpm();
+ int beat_count = playlist->audio_streams[idx]->get_beat_count();
+ double length;
+ if (bpm > 0.0 && beat_count > 0) {
+ length = beat_count * 60.0 / bpm;
+ } else {
+ length = playlist->audio_streams[idx]->get_length();
+ }
+ if (play_ofs < length) {
+ play_index = i;
+ stream_todo = length - play_ofs;
+ break;
+ } else {
+ play_ofs -= length;
+ }
+ }
+ }
+
+ if (play_index == -1) {
+ return;
+ }
+
+ playback[play_order[play_index]]->start(play_ofs);
+ fade_index = -1;
+ loop_count = 0;
+
+ active = true;
+}
+
+void AudioStreamPlaybackPlaylist::seek(double p_time) {
+ stop();
+ start(p_time);
+}
+
+int AudioStreamPlaybackPlaylist::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+ if (!active) {
+ for (int i = 0; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0.0, 0.0);
+ }
+ return p_frames;
+ }
+
+ double time_dec = (1.0 / AudioServer::get_singleton()->get_mix_rate());
+ double fade_dec = (1.0 / playlist->fade_time) / AudioServer::get_singleton()->get_mix_rate();
+
+ int todo = p_frames;
+
+ while (todo) {
+ int to_mix = MIN(todo, MIX_BUFFER_SIZE);
+
+ playback[play_order[play_index]]->mix(mix_buffer, 1.0, to_mix);
+ if (fade_index != -1) {
+ playback[fade_index]->mix(fade_buffer, 1.0, to_mix);
+ }
+
+ offset += time_dec * to_mix;
+
+ for (int i = 0; i < to_mix; i++) {
+ *p_buffer = mix_buffer[i];
+ stream_todo -= time_dec;
+ if (stream_todo < 0) {
+ //find next stream.
+ int prev = play_order[play_index];
+
+ for (int j = 0; j < playlist->stream_count; j++) {
+ play_index++;
+ if (play_index >= playlist->stream_count) {
+ // No loop, exit.
+ if (!playlist->loop) {
+ for (int k = i; k < todo - i; k++) {
+ p_buffer[k] = AudioFrame(0, 0);
+ }
+ todo = to_mix;
+ active = false;
+ break;
+ }
+
+ _update_order();
+ play_index = 0;
+ loop_count++;
+ offset = time_dec * (to_mix - i);
+ }
+ if (playback[play_order[play_index]].is_valid()) {
+ break;
+ }
+ }
+
+ if (!active) {
+ break;
+ }
+
+ if (!playback[play_order[play_index]].is_valid()) {
+ todo = to_mix; // Weird error.
+ active = false;
+ break;
+ }
+
+ bool restart = true;
+ if (prev == play_order[play_index]) {
+ // Went back to the same one, continue loop (if it loops) or restart if it does not.
+ if (playlist->audio_streams[prev]->has_loop()) {
+ restart = false;
+ }
+ fade_index = -1;
+ } else {
+ // Move current mixed data to fade buffer.
+ for (int j = i; j < to_mix; j++) {
+ fade_buffer[j] = mix_buffer[j];
+ }
+
+ fade_index = prev;
+ fade_volume = 1.0;
+ }
+
+ int idx = play_order[play_index];
+
+ if (restart) {
+ playback[idx]->start(0); // No loop, just cold-restart.
+ playback[idx]->mix(mix_buffer + i, 1.0, to_mix - i); // Fill rest of mix buffer
+ }
+
+ // Update fade todo.
+ double bpm = playlist->audio_streams[idx]->get_bpm();
+ int beat_count = playlist->audio_streams[idx]->get_beat_count();
+
+ if (bpm > 0.0 && beat_count > 0) {
+ stream_todo = beat_count * 60.0 / bpm;
+ } else {
+ stream_todo = playlist->audio_streams[idx]->get_length();
+ }
+ }
+
+ if (fade_index != -1) {
+ *p_buffer += fade_buffer[i] * fade_volume;
+ fade_volume -= fade_dec;
+ if (fade_volume <= 0.0) {
+ playback[fade_index]->stop();
+ fade_index = -1;
+ }
+ }
+
+ p_buffer++;
+ }
+
+ todo -= to_mix;
+ }
+
+ return p_frames;
+}
+
+void AudioStreamPlaybackPlaylist::tag_used_streams() {
+ if (active) {
+ playlist->audio_streams[play_order[play_index]]->tag_used(playback[play_order[play_index]]->get_playback_position());
+ }
+
+ playlist->tag_used(0);
+}
+
+int AudioStreamPlaybackPlaylist::get_loop_count() const {
+ return loop_count;
+}
+
+double AudioStreamPlaybackPlaylist::get_playback_position() const {
+ return offset;
+}
+
+bool AudioStreamPlaybackPlaylist::is_playing() const {
+ return active;
+}
+
+void AudioStreamPlaybackPlaylist::_update_playback_instances() {
+ stop();
+
+ for (int i = 0; i < playlist->stream_count; i++) {
+ if (playlist->audio_streams[i].is_valid()) {
+ playback[i] = playlist->audio_streams[i]->instantiate_playback();
+ } else {
+ playback[i].unref();
+ }
+ }
+}
diff --git a/modules/interactive_music/audio_stream_playlist.h b/modules/interactive_music/audio_stream_playlist.h
new file mode 100644
index 0000000000..a30f97b7af
--- /dev/null
+++ b/modules/interactive_music/audio_stream_playlist.h
@@ -0,0 +1,125 @@
+/**************************************************************************/
+/* audio_stream_playlist.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef AUDIO_STREAM_PLAYLIST_H
+#define AUDIO_STREAM_PLAYLIST_H
+
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamPlaybackPlaylist;
+
+class AudioStreamPlaylist : public AudioStream {
+ GDCLASS(AudioStreamPlaylist, AudioStream)
+ OBJ_SAVE_TYPE(AudioStream)
+
+private:
+ friend class AudioStreamPlaybackPlaylist;
+
+ enum {
+ MAX_STREAMS = 64
+ };
+
+ bool shuffle = false;
+ bool loop = true;
+ double fade_time = 0.3;
+
+ int stream_count = 0;
+ Ref<AudioStream> audio_streams[MAX_STREAMS];
+ HashSet<AudioStreamPlaybackPlaylist *> playbacks;
+
+public:
+ virtual double get_bpm() const override;
+ void set_stream_count(int p_count);
+ int get_stream_count() const;
+ void set_fade_time(float p_time);
+ float get_fade_time() const;
+ void set_shuffle(bool p_shuffle);
+ bool get_shuffle() const;
+ void set_loop(bool p_loop);
+ virtual bool has_loop() const override;
+ void set_list_stream(int p_stream_index, Ref<AudioStream> p_stream);
+ Ref<AudioStream> get_list_stream(int p_stream_index) const;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
+ virtual String get_stream_name() const override;
+ virtual double get_length() const override;
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &r_property) const;
+};
+
+///////////////////////////////////////
+
+class AudioStreamPlaybackPlaylist : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackPlaylist, AudioStreamPlayback)
+ friend class AudioStreamPlaylist;
+
+private:
+ enum {
+ MIX_BUFFER_SIZE = 128
+ };
+ AudioFrame mix_buffer[MIX_BUFFER_SIZE];
+ AudioFrame fade_buffer[MIX_BUFFER_SIZE];
+
+ Ref<AudioStreamPlaylist> playlist;
+ Ref<AudioStreamPlayback> playback[AudioStreamPlaylist::MAX_STREAMS];
+
+ int play_order[AudioStreamPlaylist::MAX_STREAMS] = {};
+
+ double stream_todo = 0.0;
+ int fade_index = -1;
+ double fade_volume = 1.0;
+ int play_index = 0;
+ double offset = 0.0;
+
+ int loop_count = 0;
+
+ bool active = false;
+
+ void _update_order();
+
+ void _update_playback_instances();
+
+public:
+ virtual void start(double p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+ virtual int get_loop_count() const override; // times it looped
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+
+ virtual void tag_used_streams() override;
+
+ ~AudioStreamPlaybackPlaylist();
+};
+
+#endif // AUDIO_STREAM_PLAYLIST_H
diff --git a/modules/interactive_music/audio_stream_synchronized.cpp b/modules/interactive_music/audio_stream_synchronized.cpp
new file mode 100644
index 0000000000..d0d58fac16
--- /dev/null
+++ b/modules/interactive_music/audio_stream_synchronized.cpp
@@ -0,0 +1,312 @@
+/**************************************************************************/
+/* audio_stream_synchronized.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "audio_stream_synchronized.h"
+
+#include "core/math/math_funcs.h"
+#include "core/string/print_string.h"
+
+AudioStreamSynchronized::AudioStreamSynchronized() {
+}
+
+Ref<AudioStreamPlayback> AudioStreamSynchronized::instantiate_playback() {
+ Ref<AudioStreamPlaybackSynchronized> playback_playlist;
+ playback_playlist.instantiate();
+ playback_playlist->stream = Ref<AudioStreamSynchronized>(this);
+ playback_playlist->_update_playback_instances();
+ playbacks.insert(playback_playlist.operator->());
+ return playback_playlist;
+}
+
+String AudioStreamSynchronized::get_stream_name() const {
+ return "Synchronized";
+}
+
+void AudioStreamSynchronized::set_sync_stream(int p_stream_index, Ref<AudioStream> p_stream) {
+ ERR_FAIL_COND(p_stream == this);
+ ERR_FAIL_INDEX(p_stream_index, MAX_STREAMS);
+
+ AudioServer::get_singleton()->lock();
+ audio_streams[p_stream_index] = p_stream;
+ for (AudioStreamPlaybackSynchronized *E : playbacks) {
+ E->_update_playback_instances();
+ }
+ AudioServer::get_singleton()->unlock();
+}
+
+Ref<AudioStream> AudioStreamSynchronized::get_sync_stream(int p_stream_index) const {
+ ERR_FAIL_INDEX_V(p_stream_index, MAX_STREAMS, Ref<AudioStream>());
+
+ return audio_streams[p_stream_index];
+}
+
+void AudioStreamSynchronized::set_sync_stream_volume(int p_stream_index, float p_db) {
+ ERR_FAIL_INDEX(p_stream_index, MAX_STREAMS);
+ audio_stream_volume_db[p_stream_index] = p_db;
+}
+
+float AudioStreamSynchronized::get_sync_stream_volume(int p_stream_index) const {
+ ERR_FAIL_INDEX_V(p_stream_index, MAX_STREAMS, 0);
+ return audio_stream_volume_db[p_stream_index];
+}
+
+double AudioStreamSynchronized::get_bpm() const {
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ double bpm = audio_streams[i]->get_bpm();
+ if (bpm != 0.0) {
+ return bpm;
+ }
+ }
+ }
+ return 0.0;
+}
+
+int AudioStreamSynchronized::get_beat_count() const {
+ int max_beats = 0;
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ max_beats = MAX(max_beats, audio_streams[i]->get_beat_count());
+ }
+ }
+ return max_beats;
+}
+
+bool AudioStreamSynchronized::has_loop() const {
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ if (audio_streams[i]->has_loop()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+double AudioStreamSynchronized::get_length() const {
+ double max_length = 0.0;
+ for (int i = 0; i < stream_count; i++) {
+ if (audio_streams[i].is_valid()) {
+ max_length = MAX(max_length, audio_streams[i]->get_length());
+ }
+ }
+ return max_length;
+}
+
+void AudioStreamSynchronized::set_stream_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0 || p_count > MAX_STREAMS);
+ AudioServer::get_singleton()->lock();
+ stream_count = p_count;
+ AudioServer::get_singleton()->unlock();
+ notify_property_list_changed();
+}
+
+int AudioStreamSynchronized::get_stream_count() const {
+ return stream_count;
+}
+
+void AudioStreamSynchronized::_validate_property(PropertyInfo &property) const {
+ String prop = property.name;
+ if (prop != "stream_count" && prop.begins_with("stream_")) {
+ int stream = prop.get_slicec('/', 0).get_slicec('_', 1).to_int();
+ if (stream >= stream_count) {
+ property.usage = PROPERTY_USAGE_INTERNAL;
+ }
+ }
+}
+
+void AudioStreamSynchronized::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_stream_count", "stream_count"), &AudioStreamSynchronized::set_stream_count);
+ ClassDB::bind_method(D_METHOD("get_stream_count"), &AudioStreamSynchronized::get_stream_count);
+
+ ClassDB::bind_method(D_METHOD("set_sync_stream", "stream_index", "audio_stream"), &AudioStreamSynchronized::set_sync_stream);
+ ClassDB::bind_method(D_METHOD("get_sync_stream", "stream_index"), &AudioStreamSynchronized::get_sync_stream);
+ ClassDB::bind_method(D_METHOD("set_sync_stream_volume", "stream_index", "volume_db"), &AudioStreamSynchronized::set_sync_stream_volume);
+ ClassDB::bind_method(D_METHOD("get_sync_stream_volume", "stream_index"), &AudioStreamSynchronized::get_sync_stream_volume);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stream_count", PROPERTY_HINT_RANGE, "0," + itos(MAX_STREAMS), PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Streams,stream_,unfoldable,page_size=999,add_button_text=" + String(RTR("Add Stream"))), "set_stream_count", "get_stream_count");
+
+ for (int i = 0; i < MAX_STREAMS; i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "stream_" + itos(i) + "/stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_sync_stream", "get_sync_stream", i);
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "stream_" + itos(i) + "/volume", PROPERTY_HINT_RANGE, "-60,12,0.01,suffix:db", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_INTERNAL), "set_sync_stream_volume", "get_sync_stream_volume", i);
+ }
+
+ BIND_CONSTANT(MAX_STREAMS);
+}
+
+//////////////////////
+//////////////////////
+
+AudioStreamPlaybackSynchronized::AudioStreamPlaybackSynchronized() {
+}
+
+AudioStreamPlaybackSynchronized::~AudioStreamPlaybackSynchronized() {
+ if (stream.is_valid()) {
+ stream->playbacks.erase(this);
+ }
+}
+
+void AudioStreamPlaybackSynchronized::stop() {
+ active = false;
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid()) {
+ playback[i]->stop();
+ }
+ }
+}
+
+void AudioStreamPlaybackSynchronized::start(double p_from_pos) {
+ if (active) {
+ stop();
+ }
+
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid()) {
+ playback[i]->start(p_from_pos);
+ active = true;
+ }
+ }
+}
+
+void AudioStreamPlaybackSynchronized::seek(double p_time) {
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid()) {
+ playback[i]->seek(p_time);
+ }
+ }
+}
+
+int AudioStreamPlaybackSynchronized::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
+ if (active != true) {
+ for (int i = 0; i < p_frames; i++) {
+ p_buffer[i] = AudioFrame(0.0, 0.0);
+ }
+ return p_frames;
+ }
+
+ int todo = p_frames;
+
+ bool any_active = false;
+ while (todo) {
+ int to_mix = MIN(todo, MIX_BUFFER_SIZE);
+
+ bool first = true;
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid() && playback[i]->is_playing()) {
+ float volume = Math::db_to_linear(stream->audio_stream_volume_db[i]);
+ if (first) {
+ playback[i]->mix(p_buffer, p_rate_scale, to_mix);
+ for (int j = 0; j < to_mix; j++) {
+ p_buffer[j] *= volume;
+ }
+ first = false;
+ any_active = true;
+ } else {
+ playback[i]->mix(mix_buffer, p_rate_scale, to_mix);
+ for (int j = 0; j < to_mix; j++) {
+ p_buffer[j] += mix_buffer[j] * volume;
+ }
+ }
+ }
+ }
+
+ if (first) {
+ // Nothing mixed, put zeroes.
+ for (int j = 0; j < to_mix; j++) {
+ p_buffer[j] = AudioFrame(0, 0);
+ }
+ }
+
+ p_buffer += to_mix;
+ todo -= to_mix;
+ }
+
+ if (!any_active) {
+ active = false;
+ }
+ return p_frames;
+}
+
+void AudioStreamPlaybackSynchronized::tag_used_streams() {
+ if (active) {
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid() && playback[i]->is_playing()) {
+ stream->audio_streams[i]->tag_used(playback[i]->get_playback_position());
+ }
+ }
+ stream->tag_used(0);
+ }
+}
+
+int AudioStreamPlaybackSynchronized::get_loop_count() const {
+ int min_loops = 0;
+ bool min_loops_found = false;
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid() && playback[i]->is_playing()) {
+ int loops = playback[i]->get_loop_count();
+ if (!min_loops_found || loops < min_loops) {
+ min_loops = loops;
+ min_loops_found = true;
+ }
+ }
+ }
+ return min_loops;
+}
+
+double AudioStreamPlaybackSynchronized::get_playback_position() const {
+ float max_pos = 0;
+ bool pos_found = false;
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (playback[i].is_valid() && playback[i]->is_playing()) {
+ float pos = playback[i]->get_playback_position();
+ if (!pos_found || pos > max_pos) {
+ max_pos = pos;
+ pos_found = true;
+ }
+ }
+ }
+ return max_pos;
+}
+
+bool AudioStreamPlaybackSynchronized::is_playing() const {
+ return active;
+}
+
+void AudioStreamPlaybackSynchronized::_update_playback_instances() {
+ stop();
+
+ for (int i = 0; i < stream->stream_count; i++) {
+ if (stream->audio_streams[i].is_valid()) {
+ playback[i] = stream->audio_streams[i]->instantiate_playback();
+ } else {
+ playback[i].unref();
+ }
+ }
+}
diff --git a/modules/interactive_music/audio_stream_synchronized.h b/modules/interactive_music/audio_stream_synchronized.h
new file mode 100644
index 0000000000..a2d8c55404
--- /dev/null
+++ b/modules/interactive_music/audio_stream_synchronized.h
@@ -0,0 +1,119 @@
+/**************************************************************************/
+/* audio_stream_synchronized.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef AUDIO_STREAM_SYNCHRONIZED_H
+#define AUDIO_STREAM_SYNCHRONIZED_H
+
+#include "servers/audio/audio_stream.h"
+
+class AudioStreamPlaybackSynchronized;
+
+class AudioStreamSynchronized : public AudioStream {
+ GDCLASS(AudioStreamSynchronized, AudioStream)
+ OBJ_SAVE_TYPE(AudioStream)
+
+private:
+ friend class AudioStreamPlaybackSynchronized;
+
+ enum {
+ MAX_STREAMS = 32
+ };
+
+ int stream_count = 0;
+ Ref<AudioStream> audio_streams[MAX_STREAMS];
+ float audio_stream_volume_db[MAX_STREAMS] = {};
+ HashSet<AudioStreamPlaybackSynchronized *> playbacks;
+
+public:
+ virtual double get_bpm() const override;
+ virtual int get_beat_count() const override;
+ virtual bool has_loop() const override;
+ void set_stream_count(int p_count);
+ int get_stream_count() const;
+ void set_sync_stream(int p_stream_index, Ref<AudioStream> p_stream);
+ Ref<AudioStream> get_sync_stream(int p_stream_index) const;
+ void set_sync_stream_volume(int p_stream_index, float p_db);
+ float get_sync_stream_volume(int p_stream_index) const;
+
+ virtual Ref<AudioStreamPlayback> instantiate_playback() override;
+ virtual String get_stream_name() const override;
+ virtual double get_length() const override;
+ AudioStreamSynchronized();
+
+protected:
+ static void _bind_methods();
+ void _validate_property(PropertyInfo &property) const;
+};
+
+///////////////////////////////////////
+
+class AudioStreamPlaybackSynchronized : public AudioStreamPlayback {
+ GDCLASS(AudioStreamPlaybackSynchronized, AudioStreamPlayback)
+ friend class AudioStreamSynchronized;
+
+private:
+ enum {
+ MIX_BUFFER_SIZE = 128
+ };
+ AudioFrame mix_buffer[MIX_BUFFER_SIZE];
+
+ Ref<AudioStreamSynchronized> stream;
+ Ref<AudioStreamPlayback> playback[AudioStreamSynchronized::MAX_STREAMS];
+
+ int play_order[AudioStreamSynchronized::MAX_STREAMS];
+
+ double stream_todo = 0.0;
+ int fade_index = -1;
+ double fade_volume = 1.0;
+ int play_index = 0;
+ double offset = 0.0;
+
+ int loop_count = 0;
+
+ bool active = false;
+
+ void _update_playback_instances();
+
+public:
+ virtual void start(double p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+ virtual int get_loop_count() const override; // times it looped
+ virtual double get_playback_position() const override;
+ virtual void seek(double p_time) override;
+ virtual int mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) override;
+
+ virtual void tag_used_streams() override;
+
+ AudioStreamPlaybackSynchronized();
+ ~AudioStreamPlaybackSynchronized();
+};
+
+#endif // AUDIO_STREAM_SYNCHRONIZED_H
diff --git a/modules/interactive_music/config.py b/modules/interactive_music/config.py
new file mode 100644
index 0000000000..fb327f1372
--- /dev/null
+++ b/modules/interactive_music/config.py
@@ -0,0 +1,21 @@
+def can_build(env, platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "AudioStreamPlaylist",
+ "AudioStreamPlaybackPlaylist",
+ "AudioStreamInteractive",
+ "AudioStreamPlaybackInteractive",
+ "AudioStreamSynchronized",
+ "AudioStreamPlaybackSynchronized",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/interactive_music/doc_classes/AudioStreamInteractive.xml b/modules/interactive_music/doc_classes/AudioStreamInteractive.xml
new file mode 100644
index 0000000000..e8f8e7b760
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamInteractive.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamInteractive" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Audio stream that can playback music interactively, combining clips and a transition table.
+ </brief_description>
+ <description>
+ This is an audio stream that can playback music interactively, combining clips and a transition table. Clips must be added first, and the transition rules via the [method add_transition]. Additionally, this stream export a property parameter to control the playback via [AudioStreamPlayer], [AudioStreamPlayer2D], or [AudioStreamPlayer3D].
+ The way this is used is by filling a number of clips, then configuring the transition table. From there, clips are selected for playback and the music will smoothly go from the current to the new one while using the corresponding transition rule defined in the transition table.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_transition">
+ <return type="void" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <param index="2" name="from_time" type="int" enum="AudioStreamInteractive.TransitionFromTime" />
+ <param index="3" name="to_time" type="int" enum="AudioStreamInteractive.TransitionToTime" />
+ <param index="4" name="fade_mode" type="int" enum="AudioStreamInteractive.FadeMode" />
+ <param index="5" name="fade_beats" type="float" />
+ <param index="6" name="use_filler_clip" type="bool" default="false" />
+ <param index="7" name="filler_clip" type="int" default="-1" />
+ <param index="8" name="hold_previous" type="bool" default="false" />
+ <description>
+ Add a transition between two clips. Provide the indices of the source and destination clips, or use the [constant CLIP_ANY] constant to indicate that transition happens to/from any clip to this one.
+ * [param from_time] indicates the moment in the current clip the transition will begin after triggered.
+ * [param to_time] indicates the time in the next clip that the playback will start from.
+ * [param fade_mode] indicates how the fade will happen between clips. If unsure, just use [constant FADE_AUTOMATIC] which uses the most common type of fade for each situation.
+ * [param fade_beats] indicates how many beats the fade will take. Using decimals is allowed.
+ * [param use_filler_clip] indicates that there will be a filler clip used between the source and destination clips.
+ * [param filler_clip] the index of the filler clip.
+ * If [param hold_previous] is used, then this clip will be remembered. This can be used together with [constant AUTO_ADVANCE_RETURN_TO_HOLD] to return to this clip after another is done playing.
+ </description>
+ </method>
+ <method name="erase_transition">
+ <return type="void" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Erase a transition by providing [param from_clip] and [param to_clip] clip indices. [constant CLIP_ANY] can be used for either argument or both.
+ </description>
+ </method>
+ <method name="get_clip_auto_advance" qualifiers="const">
+ <return type="int" enum="AudioStreamInteractive.AutoAdvanceMode" />
+ <param index="0" name="clip_index" type="int" />
+ <description>
+ Return whether a clip has auto-advance enabled. See [method set_clip_auto_advance].
+ </description>
+ </method>
+ <method name="get_clip_auto_advance_next_clip" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="clip_index" type="int" />
+ <description>
+ Return the clip towards which the clip referenced by [param clip_index] will auto-advance to.
+ </description>
+ </method>
+ <method name="get_clip_name" qualifiers="const">
+ <return type="StringName" />
+ <param index="0" name="clip_index" type="int" />
+ <description>
+ Return the name of a clip.
+ </description>
+ </method>
+ <method name="get_clip_stream" qualifiers="const">
+ <return type="AudioStream" />
+ <param index="0" name="clip_index" type="int" />
+ <description>
+ Return the [AudioStream] associated with a clip.
+ </description>
+ </method>
+ <method name="get_transition_fade_beats" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return the time (in beats) for a transition (see [method add_transition]).
+ </description>
+ </method>
+ <method name="get_transition_fade_mode" qualifiers="const">
+ <return type="int" enum="AudioStreamInteractive.FadeMode" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return the mode for a transition (see [method add_transition]).
+ </description>
+ </method>
+ <method name="get_transition_filler_clip" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return the filler clip for a transition (see [method add_transition]).
+ </description>
+ </method>
+ <method name="get_transition_from_time" qualifiers="const">
+ <return type="int" enum="AudioStreamInteractive.TransitionFromTime" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return the source time position for a transition (see [method add_transition]).
+ </description>
+ </method>
+ <method name="get_transition_list" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <description>
+ Return the list of transitions (from, to interleaved).
+ </description>
+ </method>
+ <method name="get_transition_to_time" qualifiers="const">
+ <return type="int" enum="AudioStreamInteractive.TransitionToTime" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return the destination time position for a transition (see [method add_transition]).
+ </description>
+ </method>
+ <method name="has_transition" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return true if a given transition exists (was added via [method add_transition]).
+ </description>
+ </method>
+ <method name="is_transition_holding_previous" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return whether a transition uses the [i]hold previous[/i] functionality (see [method add_transition]).
+ </description>
+ </method>
+ <method name="is_transition_using_filler_clip" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="from_clip" type="int" />
+ <param index="1" name="to_clip" type="int" />
+ <description>
+ Return whether a transition uses the [i]filler clip[/i] functionality (see [method add_transition]).
+ </description>
+ </method>
+ <method name="set_clip_auto_advance">
+ <return type="void" />
+ <param index="0" name="clip_index" type="int" />
+ <param index="1" name="mode" type="int" enum="AudioStreamInteractive.AutoAdvanceMode" />
+ <description>
+ Set whether a clip will auto-advance by changing the auto-advance mode.
+ </description>
+ </method>
+ <method name="set_clip_auto_advance_next_clip">
+ <return type="void" />
+ <param index="0" name="clip_index" type="int" />
+ <param index="1" name="auto_advance_next_clip" type="int" />
+ <description>
+ Set the index of the next clip towards which this clip will auto advance to when finished. If the clip being played loops, then auto-advance will be ignored.
+ </description>
+ </method>
+ <method name="set_clip_name">
+ <return type="void" />
+ <param index="0" name="clip_index" type="int" />
+ <param index="1" name="name" type="StringName" />
+ <description>
+ Set the name of the current clip (for easier identification).
+ </description>
+ </method>
+ <method name="set_clip_stream">
+ <return type="void" />
+ <param index="0" name="clip_index" type="int" />
+ <param index="1" name="stream" type="AudioStream" />
+ <description>
+ Set the [AudioStream] associated with the current clip.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="clip_count" type="int" setter="set_clip_count" getter="get_clip_count" default="0">
+ Amount of clips contained in this interactive player.
+ </member>
+ <member name="initial_clip" type="int" setter="set_initial_clip" getter="get_initial_clip" default="0">
+ Index of the initial clip, which will be played first when this stream is played.
+ </member>
+ </members>
+ <constants>
+ <constant name="TRANSITION_FROM_TIME_IMMEDIATE" value="0" enum="TransitionFromTime">
+ Start transition as soon as possible, don't wait for any specific time position.
+ </constant>
+ <constant name="TRANSITION_FROM_TIME_NEXT_BEAT" value="1" enum="TransitionFromTime">
+ Transition when the clip playback position reaches the next beat.
+ </constant>
+ <constant name="TRANSITION_FROM_TIME_NEXT_BAR" value="2" enum="TransitionFromTime">
+ Transition when the clip playback position reaches the next bar.
+ </constant>
+ <constant name="TRANSITION_FROM_TIME_END" value="3" enum="TransitionFromTime">
+ Transition when the current clip finished playing.
+ </constant>
+ <constant name="TRANSITION_TO_TIME_SAME_POSITION" value="0" enum="TransitionToTime">
+ Transition to the same position in the destination clip. This is useful when both clips have exactly the same length and the music should fade between them.
+ </constant>
+ <constant name="TRANSITION_TO_TIME_START" value="1" enum="TransitionToTime">
+ Transition to the start of the destination clip.
+ </constant>
+ <constant name="FADE_DISABLED" value="0" enum="FadeMode">
+ Do not use fade for the transition. This is useful when transitioning from a clip-end to clip-beginning, and each clip has their begin/end.
+ </constant>
+ <constant name="FADE_IN" value="1" enum="FadeMode">
+ Use a fade-in in the next clip, let the current clip finish.
+ </constant>
+ <constant name="FADE_OUT" value="2" enum="FadeMode">
+ Use a fade-out in the current clip, the next clip will start by itself.
+ </constant>
+ <constant name="FADE_CROSS" value="3" enum="FadeMode">
+ Use a cross-fade between clips.
+ </constant>
+ <constant name="FADE_AUTOMATIC" value="4" enum="FadeMode">
+ Use automatic fade logic depending on the transition from/to. It is recommended to use this by default.
+ </constant>
+ <constant name="AUTO_ADVANCE_DISABLED" value="0" enum="AutoAdvanceMode">
+ Disable auto-advance (default).
+ </constant>
+ <constant name="AUTO_ADVANCE_ENABLED" value="1" enum="AutoAdvanceMode">
+ Enable auto-advance, a clip must be specified.
+ </constant>
+ <constant name="AUTO_ADVANCE_RETURN_TO_HOLD" value="2" enum="AutoAdvanceMode">
+ Enable auto-advance, but instead of specifying a clip, the playback will return to hold (see [method add_transition]).
+ </constant>
+ <constant name="CLIP_ANY" value="-1">
+ This constant describes that any clip is valid for a specific transition as either source or destination.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/interactive_music/doc_classes/AudioStreamPlaybackInteractive.xml b/modules/interactive_music/doc_classes/AudioStreamPlaybackInteractive.xml
new file mode 100644
index 0000000000..c87d7c8fcb
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamPlaybackInteractive.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamPlaybackInteractive" inherits="AudioStreamPlayback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Playback component of [AudioStreamInteractive].
+ </brief_description>
+ <description>
+ Playback component of [AudioStreamInteractive]. Contains functions to change the currently played clip.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="switch_to_clip">
+ <return type="void" />
+ <param index="0" name="clip_index" type="int" />
+ <description>
+ Switch to a clip (by index).
+ </description>
+ </method>
+ <method name="switch_to_clip_by_name">
+ <return type="void" />
+ <param index="0" name="clip_name" type="StringName" />
+ <description>
+ Switch to a clip (by name).
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/interactive_music/doc_classes/AudioStreamPlaybackPlaylist.xml b/modules/interactive_music/doc_classes/AudioStreamPlaybackPlaylist.xml
new file mode 100644
index 0000000000..2553563071
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamPlaybackPlaylist.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamPlaybackPlaylist" inherits="AudioStreamPlayback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Playback class used for [AudioStreamPlaylist].
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/modules/interactive_music/doc_classes/AudioStreamPlaybackSynchronized.xml b/modules/interactive_music/doc_classes/AudioStreamPlaybackSynchronized.xml
new file mode 100644
index 0000000000..adaae0fef9
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamPlaybackSynchronized.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamPlaybackSynchronized" inherits="AudioStreamPlayback" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/modules/interactive_music/doc_classes/AudioStreamPlaylist.xml b/modules/interactive_music/doc_classes/AudioStreamPlaylist.xml
new file mode 100644
index 0000000000..1d429a932f
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamPlaylist.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamPlaylist" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ [AudioStream] that includes sub-streams and plays them back like a playslit.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_bpm" qualifiers="const">
+ <return type="float" />
+ <description>
+ Return the bpm of the playlist, which can vary depending on the clip being played.
+ </description>
+ </method>
+ <method name="get_list_stream" qualifiers="const">
+ <return type="AudioStream" />
+ <param index="0" name="stream_index" type="int" />
+ <description>
+ Get the stream at playback position index.
+ </description>
+ </method>
+ <method name="set_list_stream">
+ <return type="void" />
+ <param index="0" name="stream_index" type="int" />
+ <param index="1" name="audio_stream" type="AudioStream" />
+ <description>
+ Set the stream at playback position index.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="fade_time" type="float" setter="set_fade_time" getter="get_fade_time" default="0.3">
+ Fade time used when a stream ends, when going to the next one. Streams are expected to have an extra bit of audio after the end to help with fading.
+ </member>
+ <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="true">
+ If true, the playlist will loop, otherwise the playlist when end when the last stream is played.
+ </member>
+ <member name="shuffle" type="bool" setter="set_shuffle" getter="get_shuffle" default="false">
+ Shuffle the playlist. Streams are played in random order.
+ </member>
+ <member name="stream_count" type="int" setter="set_stream_count" getter="get_stream_count" default="0">
+ Amount of streams in the playlist.
+ </member>
+ </members>
+ <constants>
+ <constant name="MAX_STREAMS" value="64">
+ Maximum amount of streams supported in the playlist.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/interactive_music/doc_classes/AudioStreamSynchronized.xml b/modules/interactive_music/doc_classes/AudioStreamSynchronized.xml
new file mode 100644
index 0000000000..ea914715a3
--- /dev/null
+++ b/modules/interactive_music/doc_classes/AudioStreamSynchronized.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamSynchronized" inherits="AudioStream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Stream that can be fitted with sub-streams, which will be played in-sync.
+ </brief_description>
+ <description>
+ This is a stream that can be fitted with sub-streams, which will be played in-sync. The streams being at exactly the same time when play is pressed, and will end when the last of them ends. If one of the sub-streams loops, then playback will continue.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_sync_stream" qualifiers="const">
+ <return type="AudioStream" />
+ <param index="0" name="stream_index" type="int" />
+ <description>
+ Get one of the synchronized streams, by index.
+ </description>
+ </method>
+ <method name="get_sync_stream_volume" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="stream_index" type="int" />
+ <description>
+ Get the volume of one of the synchronized streams, by index.
+ </description>
+ </method>
+ <method name="set_sync_stream">
+ <return type="void" />
+ <param index="0" name="stream_index" type="int" />
+ <param index="1" name="audio_stream" type="AudioStream" />
+ <description>
+ Set one of the synchronized streams, by index.
+ </description>
+ </method>
+ <method name="set_sync_stream_volume">
+ <return type="void" />
+ <param index="0" name="stream_index" type="int" />
+ <param index="1" name="volume_db" type="float" />
+ <description>
+ Set the volume of one of the synchronized streams, by index.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="stream_count" type="int" setter="set_stream_count" getter="get_stream_count" default="0">
+ Set the total amount of streams that will be played back synchronized.
+ </member>
+ </members>
+ <constants>
+ <constant name="MAX_STREAMS" value="32">
+ Maximum amount of streams that can be synchrohized.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp
new file mode 100644
index 0000000000..9960c4e07c
--- /dev/null
+++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.cpp
@@ -0,0 +1,416 @@
+/**************************************************************************/
+/* audio_stream_interactive_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "audio_stream_interactive_editor_plugin.h"
+
+#include "../audio_stream_interactive.h"
+#include "core/input/input.h"
+#include "core/os/keyboard.h"
+#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
+#include "editor/editor_string_names.h"
+#include "editor/editor_undo_redo_manager.h"
+#include "editor/themes/editor_scale.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/spin_box.h"
+#include "scene/gui/split_container.h"
+#include "scene/gui/tree.h"
+
+void AudioStreamInteractiveTransitionEditor::_notification(int p_what) {
+ if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_THEME_CHANGED) {
+ fade_mode->clear();
+ fade_mode->add_icon_item(get_editor_theme_icon(SNAME("FadeDisabled")), TTR("Disabled"), AudioStreamInteractive::FADE_DISABLED);
+ fade_mode->add_icon_item(get_editor_theme_icon(SNAME("FadeIn")), TTR("Fade-In"), AudioStreamInteractive::FADE_IN);
+ fade_mode->add_icon_item(get_editor_theme_icon(SNAME("FadeOut")), TTR("Fade-Out"), AudioStreamInteractive::FADE_OUT);
+ fade_mode->add_icon_item(get_editor_theme_icon(SNAME("FadeCross")), TTR("Cross-Fade"), AudioStreamInteractive::FADE_CROSS);
+ fade_mode->add_icon_item(get_editor_theme_icon(SNAME("AutoPlay")), TTR("Automatic"), AudioStreamInteractive::FADE_AUTOMATIC);
+ }
+}
+
+void AudioStreamInteractiveTransitionEditor::_bind_methods() {
+ ClassDB::bind_method("_update_transitions", &AudioStreamInteractiveTransitionEditor::_update_transitions);
+}
+
+void AudioStreamInteractiveTransitionEditor::_edited() {
+ if (updating) {
+ return;
+ }
+
+ bool enabled = transition_enabled->is_pressed();
+ AudioStreamInteractive::TransitionFromTime from = AudioStreamInteractive::TransitionFromTime(transition_from->get_selected());
+ AudioStreamInteractive::TransitionToTime to = AudioStreamInteractive::TransitionToTime(transition_to->get_selected());
+ AudioStreamInteractive::FadeMode fade = AudioStreamInteractive::FadeMode(fade_mode->get_selected());
+ float beats = fade_beats->get_value();
+ bool use_filler = filler_clip->get_selected() > 0;
+ int filler = use_filler ? filler_clip->get_selected() - 1 : 0;
+ bool hold = hold_previous->is_pressed();
+
+ EditorUndoRedoManager::get_singleton()->create_action("Edit Transitions");
+ for (int i = 0; i < selected.size(); i++) {
+ if (!enabled) {
+ if (audio_stream_interactive->has_transition(selected[i].x, selected[i].y)) {
+ EditorUndoRedoManager::get_singleton()->add_do_method(audio_stream_interactive, "erase_transition", selected[i].x, selected[i].y);
+ }
+ } else {
+ EditorUndoRedoManager::get_singleton()->add_do_method(audio_stream_interactive, "add_transition", selected[i].x, selected[i].y, from, to, fade, beats, use_filler, filler, hold);
+ }
+ }
+ EditorUndoRedoManager::get_singleton()->add_undo_property(audio_stream_interactive, "_transitions", audio_stream_interactive->get("_transitions"));
+ EditorUndoRedoManager::get_singleton()->add_do_method(this, "_update_transitions");
+ EditorUndoRedoManager::get_singleton()->add_undo_method(this, "_update_transitions");
+ EditorUndoRedoManager::get_singleton()->commit_action();
+}
+
+void AudioStreamInteractiveTransitionEditor::_update_selection() {
+ updating_selection = false;
+ int clip_count = audio_stream_interactive->get_clip_count();
+ selected.clear();
+ Vector2i editing;
+ int editing_order = -1;
+ for (int i = 0; i <= clip_count; i++) {
+ for (int j = 0; j <= clip_count; j++) {
+ if (rows[i]->is_selected(j)) {
+ Vector2i meta = rows[i]->get_metadata(j);
+ if (selection_order.has(meta)) {
+ int order = selection_order[meta];
+ if (order > editing_order) {
+ editing = meta;
+ }
+ }
+ selected.push_back(meta);
+ }
+ }
+ }
+
+ transition_enabled->set_disabled(selected.is_empty());
+ transition_from->set_disabled(selected.is_empty());
+ transition_to->set_disabled(selected.is_empty());
+ fade_mode->set_disabled(selected.is_empty());
+ fade_beats->set_editable(!selected.is_empty());
+ filler_clip->set_disabled(selected.is_empty());
+ hold_previous->set_disabled(selected.is_empty());
+
+ if (selected.size() == 0) {
+ return;
+ }
+
+ updating = true;
+ if (!audio_stream_interactive->has_transition(editing.x, editing.y)) {
+ transition_enabled->set_pressed(false);
+ transition_from->select(0);
+ transition_to->select(0);
+ fade_mode->select(AudioStreamInteractive::FADE_AUTOMATIC);
+ fade_beats->set_value(1.0);
+ filler_clip->select(0);
+ hold_previous->set_pressed(false);
+ } else {
+ transition_enabled->set_pressed(true);
+ transition_from->select(audio_stream_interactive->get_transition_from_time(editing.x, editing.y));
+ transition_to->select(audio_stream_interactive->get_transition_to_time(editing.x, editing.y));
+ fade_mode->select(audio_stream_interactive->get_transition_fade_mode(editing.x, editing.y));
+ fade_beats->set_value(audio_stream_interactive->get_transition_fade_beats(editing.x, editing.y));
+ if (audio_stream_interactive->is_transition_using_filler_clip(editing.x, editing.y)) {
+ filler_clip->select(audio_stream_interactive->get_transition_filler_clip(editing.x, editing.y) + 1);
+ } else {
+ filler_clip->select(0);
+ }
+ hold_previous->set_pressed(audio_stream_interactive->is_transition_holding_previous(editing.x, editing.y));
+ }
+ updating = false;
+}
+
+void AudioStreamInteractiveTransitionEditor::_cell_selected(TreeItem *p_item, int p_column, bool p_selected) {
+ int to = p_item->get_meta("to");
+ int from = p_column == audio_stream_interactive->get_clip_count() ? AudioStreamInteractive::CLIP_ANY : p_column;
+ if (p_selected) {
+ selection_order[Vector2i(from, to)] = order_counter++;
+ }
+
+ if (!updating_selection) {
+ MessageQueue::get_singleton()->push_callable(callable_mp(this, &AudioStreamInteractiveTransitionEditor::_update_selection));
+ updating_selection = true;
+ }
+}
+
+void AudioStreamInteractiveTransitionEditor::_update_transitions() {
+ if (!is_visible()) {
+ return;
+ }
+ int clip_count = audio_stream_interactive->get_clip_count();
+ Color font_color = tree->get_theme_color("font_color", "Tree");
+ Color font_color_default = font_color;
+ font_color_default.a *= 0.5;
+ Ref<Texture> fade_icons[5] = {
+ get_editor_theme_icon(SNAME("FadeDisabled")),
+ get_editor_theme_icon(SNAME("FadeIn")),
+ get_editor_theme_icon(SNAME("FadeOut")),
+ get_editor_theme_icon(SNAME("FadeCross")),
+ get_editor_theme_icon(SNAME("AutoPlay"))
+ };
+ for (int i = 0; i <= clip_count; i++) {
+ for (int j = 0; j <= clip_count; j++) {
+ String txt;
+ int from = i == clip_count ? AudioStreamInteractive::CLIP_ANY : i;
+ int to = j == clip_count ? AudioStreamInteractive::CLIP_ANY : j;
+
+ bool exists = audio_stream_interactive->has_transition(from, to);
+ String tooltip;
+ Ref<Texture> icon;
+ if (!exists) {
+ if (audio_stream_interactive->has_transition(AudioStreamInteractive::CLIP_ANY, to)) {
+ from = AudioStreamInteractive::CLIP_ANY;
+ tooltip = "Using Any Clip -> " + audio_stream_interactive->get_clip_name(to) + ".";
+ } else if (audio_stream_interactive->has_transition(from, AudioStreamInteractive::CLIP_ANY)) {
+ to = AudioStreamInteractive::CLIP_ANY;
+ tooltip = "Using " + audio_stream_interactive->get_clip_name(from) + " -> Any Clip.";
+ } else if (audio_stream_interactive->has_transition(AudioStreamInteractive::CLIP_ANY, AudioStreamInteractive::CLIP_ANY)) {
+ from = to = AudioStreamInteractive::CLIP_ANY;
+ tooltip = "Using All CLips -> Any Clip.";
+ } else {
+ tooltip = "No transition available.";
+ }
+ }
+
+ if (audio_stream_interactive->has_transition(from, to)) {
+ icon = fade_icons[audio_stream_interactive->get_transition_fade_mode(from, to)];
+ switch (audio_stream_interactive->get_transition_from_time(from, to)) {
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_IMMEDIATE: {
+ txt += TTR("Immediate");
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BEAT: {
+ txt += TTR("Next Beat");
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BAR: {
+ txt += TTR("Next Bar");
+ } break;
+ case AudioStreamInteractive::TRANSITION_FROM_TIME_END: {
+ txt += TTR("Clip End");
+ } break;
+ default: {
+ }
+ }
+
+ switch (audio_stream_interactive->get_transition_to_time(from, to)) {
+ case AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION: {
+ txt += TTR(L"⮕ Same");
+ } break;
+ case AudioStreamInteractive::TRANSITION_TO_TIME_START: {
+ txt += TTR(L"⮕ Start");
+ } break;
+ case AudioStreamInteractive::TRANSITION_TO_TIME_PREVIOUS_POSITION: {
+ txt += TTR(L"⮕ Prev");
+ } break;
+ default: {
+ }
+ }
+ }
+
+ rows[j]->set_icon(i, icon);
+ rows[j]->set_text(i, txt);
+ rows[j]->set_tooltip_text(i, tooltip);
+ if (exists) {
+ rows[j]->set_custom_color(i, font_color);
+ rows[j]->set_icon_modulate(i, Color(1, 1, 1, 1));
+ } else {
+ rows[j]->set_custom_color(i, font_color_default);
+ rows[j]->set_icon_modulate(i, Color(1, 1, 1, 0.5));
+ }
+ }
+ }
+}
+
+void AudioStreamInteractiveTransitionEditor::edit(Object *p_obj) {
+ audio_stream_interactive = Object::cast_to<AudioStreamInteractive>(p_obj);
+ if (!audio_stream_interactive) {
+ return;
+ }
+
+ Ref<Font> header_font = get_theme_font("bold", "EditorFonts");
+ int header_font_size = get_theme_font_size("bold_size", "EditorFonts");
+
+ tree->clear();
+ rows.clear();
+ selection_order.clear();
+ selected.clear();
+
+ int clip_count = audio_stream_interactive->get_clip_count();
+ tree->set_columns(clip_count + 2);
+ TreeItem *root = tree->create_item();
+ TreeItem *header = tree->create_item(root); // Header
+ int header_index = clip_count + 1;
+ header->set_text(header_index, TTR("From / To"));
+ header->set_editable(0, false);
+
+ filler_clip->clear();
+ filler_clip->add_item("Disabled", -1);
+
+ Color header_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
+
+ int max_w = 0;
+
+ updating = true;
+ for (int i = 0; i <= clip_count; i++) {
+ int cell_index = i;
+ int clip_i = i == clip_count ? AudioStreamInteractive::CLIP_ANY : i;
+ header->set_editable(cell_index, false);
+ header->set_selectable(cell_index, false);
+ header->set_custom_font(cell_index, header_font);
+ header->set_custom_font_size(cell_index, header_font_size);
+ header->set_custom_bg_color(cell_index, header_color);
+
+ String name;
+ if (i == clip_count) {
+ name = TTR("Any Clip");
+ } else {
+ name = audio_stream_interactive->get_clip_name(i);
+ }
+
+ int min_w = header_font->get_string_size(name + "XX").width;
+ tree->set_column_expand(cell_index, false);
+ tree->set_column_custom_minimum_width(cell_index, min_w);
+ max_w = MAX(max_w, min_w);
+
+ header->set_text(cell_index, name);
+
+ TreeItem *row = tree->create_item(root);
+ row->set_text(header_index, name);
+ row->set_selectable(header_index, false);
+ row->set_custom_font(header_index, header_font);
+ row->set_custom_font_size(header_index, header_font_size);
+ row->set_custom_bg_color(header_index, header_color);
+ row->set_meta("to", clip_i);
+ for (int j = 0; j <= clip_count; j++) {
+ int clip_j = j == clip_count ? AudioStreamInteractive::CLIP_ANY : j;
+ row->set_metadata(j, Vector2i(clip_j, clip_i));
+ }
+ rows.push_back(row);
+
+ if (i < clip_count) {
+ filler_clip->add_item(name, i);
+ }
+ }
+
+ tree->set_column_expand(header_index, false);
+ tree->set_column_custom_minimum_width(header_index, max_w);
+ selection_order.clear();
+ _update_selection();
+ popup_centered_ratio(0.6);
+ updating = false;
+ _update_transitions();
+}
+
+AudioStreamInteractiveTransitionEditor::AudioStreamInteractiveTransitionEditor() {
+ set_title(TTR("AudioStreamInteractive Transition Editor"));
+ split = memnew(HSplitContainer);
+ add_child(split);
+ tree = memnew(Tree);
+ tree->set_hide_root(true);
+ tree->add_theme_constant_override("draw_guides", 1);
+ tree->set_select_mode(Tree::SELECT_MULTI);
+ split->add_child(tree);
+
+ tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tree->connect("multi_selected", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_cell_selected));
+ VBoxContainer *edit_vb = memnew(VBoxContainer);
+ split->add_child(edit_vb);
+
+ transition_enabled = memnew(CheckBox);
+ transition_enabled->set_text(TTR("Use Transition"));
+ edit_vb->add_margin_child(TTR("Transition Enabled:"), transition_enabled);
+ transition_enabled->connect("pressed", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited));
+
+ transition_from = memnew(OptionButton);
+ edit_vb->add_margin_child(TTR("Transition From:"), transition_from);
+ transition_from->add_item(TTR("Immediate"), AudioStreamInteractive::TRANSITION_FROM_TIME_IMMEDIATE);
+ transition_from->add_item(TTR("Next Beat"), AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BEAT);
+ transition_from->add_item(TTR("Next Bar"), AudioStreamInteractive::TRANSITION_FROM_TIME_NEXT_BAR);
+ transition_from->add_item(TTR("Clip End"), AudioStreamInteractive::TRANSITION_FROM_TIME_END);
+
+ transition_from->connect("item_selected", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited).unbind(1));
+
+ transition_to = memnew(OptionButton);
+ edit_vb->add_margin_child(TTR("Transition To:"), transition_to);
+ transition_to->add_item(TTR("Same Position"), AudioStreamInteractive::TRANSITION_TO_TIME_SAME_POSITION);
+ transition_to->add_item(TTR("Clip Start"), AudioStreamInteractive::TRANSITION_TO_TIME_START);
+ transition_to->add_item(TTR("Prev Position"), AudioStreamInteractive::TRANSITION_TO_TIME_PREVIOUS_POSITION);
+ transition_to->connect("item_selected", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited).unbind(1));
+
+ fade_mode = memnew(OptionButton);
+ edit_vb->add_margin_child(TTR("Fade Mode:"), fade_mode);
+ fade_mode->connect("item_selected", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited).unbind(1));
+
+ fade_beats = memnew(SpinBox);
+ edit_vb->add_margin_child(TTR("Fade Beats:"), fade_beats);
+ fade_beats->set_max(16);
+ fade_beats->set_step(0.1);
+ fade_beats->connect("value_changed", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited).unbind(1));
+
+ filler_clip = memnew(OptionButton);
+ edit_vb->add_margin_child(TTR("Filler Clip:"), filler_clip);
+ filler_clip->connect("item_selected", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited).unbind(1));
+
+ hold_previous = memnew(CheckBox);
+ hold_previous->set_text(TTR("Enabled"));
+ hold_previous->connect("pressed", callable_mp(this, &AudioStreamInteractiveTransitionEditor::_edited));
+ edit_vb->add_margin_child(TTR("Hold Previous:"), hold_previous);
+
+ set_exclusive(true);
+}
+
+////////////////////////
+
+bool EditorInspectorPluginAudioStreamInteractive::can_handle(Object *p_object) {
+ return Object::cast_to<AudioStreamInteractive>(p_object);
+}
+
+void EditorInspectorPluginAudioStreamInteractive::_edit(Object *p_object) {
+ audio_stream_interactive_transition_editor->edit(p_object);
+}
+
+void EditorInspectorPluginAudioStreamInteractive::parse_end(Object *p_object) {
+ if (Object::cast_to<AudioStreamInteractive>(p_object)) {
+ Button *button = EditorInspector::create_inspector_action_button(TTR("Edit Transitions"));
+ button->set_icon(audio_stream_interactive_transition_editor->get_editor_theme_icon(SNAME("Blend")));
+ button->connect("pressed", callable_mp(this, &EditorInspectorPluginAudioStreamInteractive::_edit).bind(p_object));
+ add_custom_control(button);
+ }
+}
+
+EditorInspectorPluginAudioStreamInteractive::EditorInspectorPluginAudioStreamInteractive() {
+ audio_stream_interactive_transition_editor = memnew(AudioStreamInteractiveTransitionEditor);
+ EditorNode::get_singleton()->get_gui_base()->add_child(audio_stream_interactive_transition_editor);
+}
+
+AudioStreamInteractiveEditorPlugin::AudioStreamInteractiveEditorPlugin() {
+ Ref<EditorInspectorPluginAudioStreamInteractive> inspector_plugin;
+ inspector_plugin.instantiate();
+ add_inspector_plugin(inspector_plugin);
+}
diff --git a/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
new file mode 100644
index 0000000000..730d1ca83b
--- /dev/null
+++ b/modules/interactive_music/editor/audio_stream_interactive_editor_plugin.h
@@ -0,0 +1,110 @@
+/**************************************************************************/
+/* audio_stream_interactive_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
+#define AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
+
+#include "editor/editor_inspector.h"
+#include "editor/editor_plugin.h"
+#include "scene/gui/dialogs.h"
+
+class CheckBox;
+class HSplitContainer;
+class VSplitContainer;
+class Tree;
+class TreeItem;
+class AudioStreamInteractive;
+
+class AudioStreamInteractiveTransitionEditor : public AcceptDialog {
+ GDCLASS(AudioStreamInteractiveTransitionEditor, AcceptDialog);
+
+ AudioStreamInteractive *audio_stream_interactive = nullptr;
+
+ HSplitContainer *split = nullptr;
+ Tree *tree = nullptr;
+
+ Vector<TreeItem *> rows;
+
+ CheckBox *transition_enabled = nullptr;
+ OptionButton *transition_from = nullptr;
+ OptionButton *transition_to = nullptr;
+ OptionButton *fade_mode = nullptr;
+ SpinBox *fade_beats = nullptr;
+ OptionButton *filler_clip = nullptr;
+ CheckBox *hold_previous = nullptr;
+
+ bool updating_selection = false;
+ int order_counter = 0;
+ HashMap<Vector2i, int> selection_order;
+
+ Vector<Vector2i> selected;
+ bool updating = false;
+ void _cell_selected(TreeItem *p_item, int p_column, bool p_selected);
+ void _update_transitions();
+
+ void _update_selection();
+ void _edited();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void edit(Object *p_obj);
+
+ AudioStreamInteractiveTransitionEditor();
+};
+
+//
+
+class EditorInspectorPluginAudioStreamInteractive : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginAudioStreamInteractive, EditorInspectorPlugin);
+
+ AudioStreamInteractiveTransitionEditor *audio_stream_interactive_transition_editor = nullptr;
+
+ void _edit(Object *p_object);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual void parse_end(Object *p_object) override;
+
+ EditorInspectorPluginAudioStreamInteractive();
+};
+
+class AudioStreamInteractiveEditorPlugin : public EditorPlugin {
+ GDCLASS(AudioStreamInteractiveEditorPlugin, EditorPlugin);
+
+public:
+ virtual String get_name() const override { return "AudioStreamInteractive"; }
+
+ AudioStreamInteractiveEditorPlugin();
+};
+
+#endif // AUDIO_STREAM_INTERACTIVE_EDITOR_PLUGIN_H
diff --git a/modules/interactive_music/register_types.cpp b/modules/interactive_music/register_types.cpp
new file mode 100644
index 0000000000..5baea13f81
--- /dev/null
+++ b/modules/interactive_music/register_types.cpp
@@ -0,0 +1,60 @@
+/**************************************************************************/
+/* register_types.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "register_types.h"
+
+#include "audio_stream_interactive.h"
+#include "audio_stream_playlist.h"
+#include "audio_stream_synchronized.h"
+#include "core/object/class_db.h"
+
+#ifdef TOOLS_ENABLED
+#include "editor/audio_stream_interactive_editor_plugin.h"
+#endif
+
+void initialize_interactive_music_module(ModuleInitializationLevel p_level) {
+ if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
+ GDREGISTER_CLASS(AudioStreamPlaylist);
+ GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackPlaylist);
+ GDREGISTER_CLASS(AudioStreamInteractive);
+ GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackInteractive);
+ GDREGISTER_CLASS(AudioStreamSynchronized);
+ GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackSynchronized);
+ }
+#ifdef TOOLS_ENABLED
+ if (p_level == MODULE_INITIALIZATION_LEVEL_EDITOR) {
+ EditorPlugins::add_by_type<AudioStreamInteractiveEditorPlugin>();
+ }
+#endif
+}
+
+void uninitialize_interactive_music_module(ModuleInitializationLevel p_level) {
+ // Nothing to do here.
+}
diff --git a/modules/interactive_music/register_types.h b/modules/interactive_music/register_types.h
new file mode 100644
index 0000000000..5625e28b64
--- /dev/null
+++ b/modules/interactive_music/register_types.h
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/* register_types.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef INTERACTIVE_MUSIC_REGISTER_TYPES_H
+#define INTERACTIVE_MUSIC_REGISTER_TYPES_H
+
+#include "modules/register_module_types.h"
+
+void initialize_interactive_music_module(ModuleInitializationLevel p_level);
+void uninitialize_interactive_music_module(ModuleInitializationLevel p_level);
+
+#endif // INTERACTIVE_MUSIC_REGISTER_TYPES_H
diff --git a/modules/ktx/texture_loader_ktx.cpp b/modules/ktx/texture_loader_ktx.cpp
index 026c0ce510..7c73f9cb8a 100644
--- a/modules/ktx/texture_loader_ktx.cpp
+++ b/modules/ktx/texture_loader_ktx.cpp
@@ -90,7 +90,7 @@ static Ref<Image> load_from_file_access(Ref<FileAccess> f, Error *r_error) {
ktx_stream.destruct = ktx_destruct;
ktx_stream.type = eStreamTypeCustom;
ktx_stream.data.custom_ptr.address = &f;
- ktx_stream.data.custom_ptr.allocatorAddress = NULL;
+ ktx_stream.data.custom_ptr.allocatorAddress = nullptr;
ktx_stream.data.custom_ptr.size = 0;
ktx_stream.readpos = 0;
ktx_stream.closeOnDestruct = false;
diff --git a/modules/mono/editor/hostfxr_resolver.cpp b/modules/mono/editor/hostfxr_resolver.cpp
index 4f15335c1e..7fa482969e 100644
--- a/modules/mono/editor/hostfxr_resolver.cpp
+++ b/modules/mono/editor/hostfxr_resolver.cpp
@@ -239,7 +239,7 @@ bool get_dotnet_self_registered_dir(String &r_dotnet_root) {
String sub_key = "SOFTWARE\\dotnet\\Setup\\InstalledVersions\\" + get_dotnet_arch();
Char16String value = String("InstallLocation").utf16();
- HKEY hkey = NULL;
+ HKEY hkey = nullptr;
LSTATUS result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(sub_key.utf16().get_data()), 0, KEY_READ | KEY_WOW64_32KEY, &hkey);
if (result != ERROR_SUCCESS) {
return false;
diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub
index 0c269bc7f4..f4316a74e7 100644
--- a/modules/msdfgen/SCsub
+++ b/modules/msdfgen/SCsub
@@ -20,8 +20,6 @@ if env["builtin_msdfgen"]:
"core/Projection.cpp",
"core/Scanline.cpp",
"core/Shape.cpp",
- "core/SignedDistance.cpp",
- "core/Vector2.cpp",
"core/contour-combiners.cpp",
"core/edge-coloring.cpp",
"core/edge-segments.cpp",
@@ -36,6 +34,7 @@ if env["builtin_msdfgen"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+ env_msdfgen.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env_msdfgen.Prepend(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"])
lib = env_msdfgen.add_library("msdfgen_builtin", thirdparty_sources)
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index d3e6eb01ce..e1a04a0796 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -118,7 +118,7 @@ private:
XrSession session = XR_NULL_HANDLE;
XrSessionState session_state = XR_SESSION_STATE_UNKNOWN;
bool running = false;
- XrFrameState frame_state = { XR_TYPE_FRAME_STATE, NULL, 0, 0, false };
+ XrFrameState frame_state = { XR_TYPE_FRAME_STATE, nullptr, 0, 0, false };
double render_target_size_multiplier = 1.0;
OpenXRGraphicsExtensionWrapper *graphics_extension = nullptr;
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 6b53353ec9..b04ad77bc9 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -531,6 +531,7 @@ module_obj = []
if env["builtin_msdfgen"] and msdfgen_enabled:
# Treat msdfgen headers as system headers to avoid raising warnings. Not supported on MSVC.
+ env_text_server_adv.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
if not env.msvc:
env_text_server_adv.Append(CPPFLAGS=["-isystem", Dir("#thirdparty/msdfgen").path])
else:
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index cd4331b60e..1f27f5ade9 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -133,8 +133,6 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
"core/Projection.cpp",
"core/Scanline.cpp",
"core/Shape.cpp",
- "core/SignedDistance.cpp",
- "core/Vector2.cpp",
"core/contour-combiners.cpp",
"core/edge-coloring.cpp",
"core/edge-segments.cpp",
@@ -149,8 +147,10 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
]
thirdparty_msdfgen_sources = [thirdparty_msdfgen_dir + file for file in thirdparty_msdfgen_sources]
+ env_msdfgen.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env_msdfgen.Append(CPPPATH=["../../../thirdparty/freetype/include", "../../../thirdparty/msdfgen"])
env.Append(CPPPATH=["../../../thirdparty/msdfgen"])
+ env.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env.Append(CPPDEFINES=["MODULE_MSDFGEN_ENABLED"])
lib = env_msdfgen.Library(
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index bb198c9c2b..fc06e24bee 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -67,10 +67,16 @@ using namespace godot;
// Thirdparty headers.
#ifdef MODULE_MSDFGEN_ENABLED
+#ifdef _MSC_VER
+#pragma warning(disable : 4458)
+#endif
#include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.h>
#include <core/edge-selectors.h>
#include <msdfgen.h>
+#ifdef _MSC_VER
+#pragma warning(default : 4458)
+#endif
#endif
#ifdef MODULE_SVG_ENABLED
@@ -6820,13 +6826,13 @@ int64_t TextServerAdvanced::_is_confusable(const String &p_string, const PackedS
}
for (int i = 0; i < p_dict.size(); i++) {
Char16String word = p_dict[i].utf16();
- int32_t len = uspoof_getSkeleton(sc_conf, 0, word.get_data(), -1, NULL, 0, &status);
+ int32_t len = uspoof_getSkeleton(sc_conf, 0, word.get_data(), -1, nullptr, 0, &status);
skeletons.write[i] = (UChar *)memalloc(++len * sizeof(UChar));
status = U_ZERO_ERROR;
uspoof_getSkeleton(sc_conf, 0, word.get_data(), -1, skeletons.write[i], len, &status);
}
- int32_t len = uspoof_getSkeleton(sc_conf, 0, utf16.get_data(), -1, NULL, 0, &status);
+ int32_t len = uspoof_getSkeleton(sc_conf, 0, utf16.get_data(), -1, nullptr, 0, &status);
UChar *skel = (UChar *)memalloc(++len * sizeof(UChar));
status = U_ZERO_ERROR;
uspoof_getSkeleton(sc_conf, 0, utf16.get_data(), -1, skel, len, &status);
@@ -6867,7 +6873,7 @@ bool TextServerAdvanced::_spoof_check(const String &p_string) const {
uspoof_setRestrictionLevel(sc_spoof, USPOOF_MODERATELY_RESTRICTIVE);
}
- int32_t bitmask = uspoof_check(sc_spoof, utf16.get_data(), -1, NULL, &status);
+ int32_t bitmask = uspoof_check(sc_spoof, utf16.get_data(), -1, nullptr, &status);
ERR_FAIL_COND_V_MSG(U_FAILURE(status), false, u_errorName(status));
return (bitmask != 0);
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
index e808864512..fc0a8727c6 100644
--- a/modules/text_server_fb/SCsub
+++ b/modules/text_server_fb/SCsub
@@ -17,6 +17,7 @@ if "svg" in env.module_list:
if env["builtin_msdfgen"] and msdfgen_enabled:
# Treat msdfgen headers as system headers to avoid raising warnings. Not supported on MSVC.
+ env_text_server_fb.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
if not env.msvc:
env_text_server_fb.Append(CPPFLAGS=["-isystem", Dir("#thirdparty/msdfgen").path])
else:
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 0efced0bfc..29801ede8e 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -144,8 +144,10 @@ if env["msdfgen_enabled"] and env["freetype_enabled"]:
]
thirdparty_msdfgen_sources = [thirdparty_msdfgen_dir + file for file in thirdparty_msdfgen_sources]
+ env_msdfgen.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env_msdfgen.Append(CPPPATH=["../../../thirdparty/freetype/include", "../../../thirdparty/msdfgen"])
env.Append(CPPPATH=["../../../thirdparty/msdfgen"])
+ env.Append(CPPDEFINES=[("MSDFGEN_PUBLIC", "")])
env.Append(CPPDEFINES=["MODULE_MSDFGEN_ENABLED"])
lib = env_msdfgen.Library(
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 1a63e5ac42..4c0ed084d8 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -59,10 +59,16 @@ using namespace godot;
// Thirdparty headers.
#ifdef MODULE_MSDFGEN_ENABLED
+#ifdef _MSC_VER
+#pragma warning(disable : 4458)
+#endif
#include <core/ShapeDistanceFinder.h>
#include <core/contour-combiners.h>
#include <core/edge-selectors.h>
#include <msdfgen.h>
+#ifdef _MSC_VER
+#pragma warning(default : 4458)
+#endif
#endif
#ifdef MODULE_SVG_ENABLED
@@ -73,8 +79,6 @@ using namespace godot;
/*************************************************************************/
-#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1) & 0xff) << 24) | (((uint32_t)(c2) & 0xff) << 16) | (((uint32_t)(c3) & 0xff) << 8) | ((uint32_t)(c4) & 0xff)))
-
bool TextServerFallback::_has_feature(Feature p_feature) const {
switch (p_feature) {
case FEATURE_SIMPLE_LAYOUT:
@@ -907,8 +911,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *p_f
coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
- if (p_font_data->variation_coordinates.has(_tag_to_name(var_tag))) {
- var_value = p_font_data->variation_coordinates[_tag_to_name(var_tag)];
+ if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) {
+ var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)];
coords.write[i] = CLAMP(var_value * 65536.0, amaster->axis[i].minimum, amaster->axis[i].maximum);
}
}
@@ -3704,9 +3708,9 @@ RID TextServerFallback::_find_sys_font_for_text(const RID &p_fdef, const String
int font_weight = _font_get_weight(p_fdef);
int font_stretch = _font_get_stretch(p_fdef);
Dictionary dvar = _font_get_variation_coordinates(p_fdef);
- static int64_t wgth_tag = _name_to_tag("weight");
- static int64_t wdth_tag = _name_to_tag("width");
- static int64_t ital_tag = _name_to_tag("italic");
+ static int64_t wgth_tag = name_to_tag("weight");
+ static int64_t wdth_tag = name_to_tag("width");
+ static int64_t ital_tag = name_to_tag("italic");
if (dvar.has(wgth_tag)) {
font_weight = dvar[wgth_tag].operator int();
}
diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm
index 23614af366..ac13a4d457 100644
--- a/platform/ios/os_ios.mm
+++ b/platform/ios/os_ios.mm
@@ -361,7 +361,7 @@ String OS_IOS::get_unique_id() const {
String OS_IOS::get_processor_name() const {
char buffer[256];
size_t buffer_len = 256;
- if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, NULL, 0) == 0) {
+ if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, nullptr, 0) == 0) {
return String::utf8(buffer, buffer_len);
}
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
diff --git a/platform/linuxbsd/wayland/detect_prime_egl.cpp b/platform/linuxbsd/wayland/detect_prime_egl.cpp
index 4bee32ae3a..4c97a80039 100644
--- a/platform/linuxbsd/wayland/detect_prime_egl.cpp
+++ b/platform/linuxbsd/wayland/detect_prime_egl.cpp
@@ -69,7 +69,7 @@ void DetectPrimeEGL::create_context() {
EGLConfig egl_config;
EGLContext egl_context = EGL_NO_CONTEXT;
- eglInitialize(egl_display, NULL, NULL);
+ eglInitialize(egl_display, nullptr, nullptr);
#if defined(GLAD_ENABLED)
if (!gladLoaderLoadEGL(egl_display)) {
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index 0216ad862c..5040f5dd45 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -1724,7 +1724,7 @@ void WaylandThread::_wl_keyboard_on_keymap(void *data, struct wl_keyboard *wl_ke
ss->keymap_buffer = nullptr;
}
- ss->keymap_buffer = (const char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ ss->keymap_buffer = (const char *)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
ss->keymap_buffer_size = size;
xkb_keymap_unref(ss->xkb_keymap);
@@ -2854,7 +2854,7 @@ void WaylandThread::seat_state_lock_pointer(SeatState *p_ss) {
ERR_FAIL_NULL(locked_surface);
- p_ss->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(registry.wp_pointer_constraints, locked_surface, p_ss->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+ p_ss->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(registry.wp_pointer_constraints, locked_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
}
@@ -2886,7 +2886,7 @@ void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) {
ERR_FAIL_NULL(confined_surface);
- p_ss->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(registry.wp_pointer_constraints, confined_surface, p_ss->wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+ p_ss->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(registry.wp_pointer_constraints, confined_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
}
@@ -3660,7 +3660,7 @@ void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_c
munmap(cursor.buffer_data, cursor.buffer_data_size);
}
- cursor.buffer_data = (uint32_t *)mmap(NULL, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (cursor.wl_buffer) {
// Clean up the old Wayland buffer.
diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm
index 29bc60bd2a..c52ed00b3d 100644
--- a/platform/macos/display_server_macos.mm
+++ b/platform/macos/display_server_macos.mm
@@ -515,7 +515,7 @@ void DisplayServerMacOS::_keyboard_layout_changed(CFNotificationCenterRef center
NSImage *DisplayServerMacOS::_convert_to_nsimg(Ref<Image> &p_image) const {
p_image->convert(Image::FORMAT_RGBA8);
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
+ initWithBitmapDataPlanes:nullptr
pixelsWide:p_image->get_width()
pixelsHigh:p_image->get_height()
bitsPerSample:8
diff --git a/platform/macos/os_macos.mm b/platform/macos/os_macos.mm
index c5c95c9a70..d9ad8f937a 100644
--- a/platform/macos/os_macos.mm
+++ b/platform/macos/os_macos.mm
@@ -70,7 +70,7 @@ void OS_MacOS::initialize() {
String OS_MacOS::get_processor_name() const {
char buffer[256];
size_t buffer_len = 256;
- if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, NULL, 0) == 0) {
+ if (sysctlbyname("machdep.cpu.brand_string", &buffer, &buffer_len, nullptr, 0) == 0) {
return String::utf8(buffer, buffer_len);
}
ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name. Returning an empty string."));
diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp
index e32fbd47dd..133d36aa0d 100644
--- a/platform/windows/crash_handler_windows.cpp
+++ b/platform/windows/crash_handler_windows.cpp
@@ -109,7 +109,7 @@ public:
ret.module_name = temp;
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
- SymLoadModule64(process, 0, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
+ SymLoadModule64(process, nullptr, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
return ret;
}
};
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index eff2ec8f96..14718d8838 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -202,8 +202,8 @@ void DisplayServerWindows::_register_raw_input_devices(WindowID p_target_window)
rid[1].hwndTarget = windows[p_target_window].hWnd;
} else {
// Follow the keyboard focus
- rid[0].hwndTarget = 0;
- rid[1].hwndTarget = 0;
+ rid[0].hwndTarget = nullptr;
+ rid[1].hwndTarget = nullptr;
}
if (RegisterRawInputDevices(rid, 2, sizeof(rid[0])) == FALSE) {
@@ -272,7 +272,7 @@ public:
QITABENT(FileDialogEventHandler, IFileDialogEvents),
QITABENT(FileDialogEventHandler, IFileDialogControlEvents),
#endif
- { 0, 0 },
+ { nullptr, 0 },
};
return QISearch(this, qit, riid, ppv);
}
@@ -762,15 +762,15 @@ Ref<Image> DisplayServerWindows::clipboard_get_image() const {
}
} else if (IsClipboardFormatAvailable(CF_DIB)) {
HGLOBAL mem = GetClipboardData(CF_DIB);
- if (mem != NULL) {
+ if (mem != nullptr) {
BITMAPINFO *ptr = static_cast<BITMAPINFO *>(GlobalLock(mem));
- if (ptr != NULL) {
+ if (ptr != nullptr) {
BITMAPINFOHEADER *info = &ptr->bmiHeader;
void *dib_bits = (void *)(ptr->bmiColors);
// Draw DIB image to temporary DC surface and read it back as BGRA8.
- HDC dc = GetDC(0);
+ HDC dc = GetDC(nullptr);
if (dc) {
HDC hdc = CreateCompatibleDC(dc);
if (hdc) {
@@ -804,7 +804,7 @@ Ref<Image> DisplayServerWindows::clipboard_get_image() const {
}
DeleteDC(hdc);
}
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
}
GlobalUnlock(mem);
}
@@ -868,7 +868,7 @@ int DisplayServerWindows::get_screen_count() const {
}
int DisplayServerWindows::get_primary_screen() const {
- EnumScreenData data = { 0, 0, 0 };
+ EnumScreenData data = { 0, 0, nullptr };
EnumDisplayMonitors(nullptr, nullptr, _MonitorEnumProcPrim, (LPARAM)&data);
return data.screen;
}
@@ -1116,16 +1116,16 @@ Color DisplayServerWindows::screen_get_pixel(const Point2i &p_position) const {
p.x = pos.x;
p.y = pos.y;
if (win81p_LogicalToPhysicalPointForPerMonitorDPI) {
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p);
}
- HDC dc = GetDC(0);
+ HDC dc = GetDC(nullptr);
if (dc) {
COLORREF col = GetPixel(dc, p.x, p.y);
if (col != CLR_INVALID) {
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
return Color(float(col & 0x000000FF) / 255.0f, float((col & 0x0000FF00) >> 8) / 255.0f, float((col & 0x00FF0000) >> 16) / 255.0f, 1.0f);
}
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
}
return Color();
@@ -1156,12 +1156,12 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
p2.x = pos.x + size.x;
p2.y = pos.y + size.y;
if (win81p_LogicalToPhysicalPointForPerMonitorDPI) {
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p1);
- win81p_LogicalToPhysicalPointForPerMonitorDPI(0, &p2);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p1);
+ win81p_LogicalToPhysicalPointForPerMonitorDPI(nullptr, &p2);
}
Ref<Image> img;
- HDC dc = GetDC(0);
+ HDC dc = GetDC(nullptr);
if (dc) {
HDC hdc = CreateCompatibleDC(dc);
int width = p2.x - p1.x;
@@ -1194,7 +1194,7 @@ Ref<Image> DisplayServerWindows::screen_get_image(int p_screen) const {
}
DeleteDC(hdc);
}
- ReleaseDC(NULL, dc);
+ ReleaseDC(nullptr, dc);
}
return img;
@@ -1420,7 +1420,7 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
wintab_WTClose(windows[p_window].wtctx);
- windows[p_window].wtctx = 0;
+ windows[p_window].wtctx = nullptr;
}
DestroyWindow(windows[p_window].hWnd);
windows.erase(p_window);
@@ -1541,7 +1541,7 @@ Size2i DisplayServerWindows::window_get_title_size(const String &p_title, Window
return size;
}
- HDC hdc = GetDCEx(wd.hWnd, NULL, DCX_WINDOW);
+ HDC hdc = GetDCEx(wd.hWnd, nullptr, DCX_WINDOW);
if (hdc) {
Char16String s = p_title.utf16();
SIZE text_size;
@@ -1559,8 +1559,8 @@ Size2i DisplayServerWindows::window_get_title_size(const String &p_title, Window
ClientToScreen(wd.hWnd, (POINT *)&rect.right);
if (win81p_PhysicalToLogicalPointForPerMonitorDPI) {
- win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.left);
- win81p_PhysicalToLogicalPointForPerMonitorDPI(0, (POINT *)&rect.right);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.left);
+ win81p_PhysicalToLogicalPointForPerMonitorDPI(nullptr, (POINT *)&rect.right);
}
size.x += (rect.right - rect.left);
@@ -1991,7 +1991,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
restore_mouse_trails = 0;
}
}
@@ -2048,7 +2048,7 @@ void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window)
// Save number of trails so we can restore when exiting, then turn off mouse trails
SystemParametersInfoA(SPI_GETMOUSETRAILS, 0, &restore_mouse_trails, 0);
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, 0, nullptr, 0);
}
}
}
@@ -2303,10 +2303,10 @@ void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p
if (p_active) {
wd.ime_active = true;
ImmAssociateContext(wd.hWnd, wd.im_himc);
- CreateCaret(wd.hWnd, NULL, 1, 1);
+ CreateCaret(wd.hWnd, nullptr, 1, 1);
window_set_ime_position(wd.im_position, p_window);
} else {
- ImmAssociateContext(wd.hWnd, (HIMC)0);
+ ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
DestroyCaret();
wd.ime_active = false;
}
@@ -2321,7 +2321,7 @@ void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowI
wd.im_position = p_pos;
HIMC himc = ImmGetContext(wd.hWnd);
- if (himc == (HIMC)0) {
+ if (himc == (HIMC) nullptr) {
return;
}
@@ -5012,7 +5012,7 @@ void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const
if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) {
wintab_WTEnable(wd.wtctx, false);
wintab_WTClose(wd.wtctx);
- wd.wtctx = 0;
+ wd.wtctx = nullptr;
}
if ((p_new_driver == "wintab") && wintab_available) {
wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc);
@@ -5101,8 +5101,6 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
dwExStyle,
L"Engine", L"",
dwStyle,
- // (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
- // (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
WindowRect.left,
WindowRect.top,
WindowRect.right - WindowRect.left,
@@ -5220,7 +5218,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
print_verbose("WinTab context creation failed.");
}
} else {
- wd.wtctx = 0;
+ wd.wtctx = nullptr;
}
if (p_mode == WINDOW_MODE_MAXIMIZED) {
@@ -5266,7 +5264,7 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
// IME.
wd.im_himc = ImmGetContext(wd.hWnd);
- ImmAssociateContext(wd.hWnd, (HIMC)0);
+ ImmAssociateContext(wd.hWnd, (HIMC) nullptr);
wd.im_position = Vector2();
@@ -5321,17 +5319,17 @@ Vector2i _get_device_ids(const String &p_device_name) {
REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator; // Interface UUID
- IWbemLocator *wbemLocator = NULL; // to get the services
- IWbemServices *wbemServices = NULL; // to get the class
- IEnumWbemClassObject *iter = NULL;
+ IWbemLocator *wbemLocator = nullptr; // to get the services
+ IWbemServices *wbemServices = nullptr; // to get the class
+ IEnumWbemClassObject *iter = nullptr;
IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
- HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
+ HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
if (hr != S_OK) {
return Vector2i();
}
BSTR resource_name = SysAllocString(L"root\\CIMV2");
- hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &wbemServices);
+ hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);
SysFreeString(resource_name);
SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`
@@ -5345,7 +5343,7 @@ Vector2i _get_device_ids(const String &p_device_name) {
const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", p_device_name);
BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());
BSTR query_lang = SysAllocString(L"WQL");
- hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &iter);
+ hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);
SysFreeString(query_lang);
SysFreeString(query);
if (hr == S_OK) {
@@ -5356,7 +5354,7 @@ Vector2i _get_device_ids(const String &p_device_name) {
VARIANT did;
VariantInit(&did);
BSTR object_name = SysAllocString(L"DeviceID");
- hr = pnpSDriverObject[0]->Get(object_name, 0, &did, NULL, NULL);
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &did, nullptr, nullptr);
SysFreeString(object_name);
if (hr == S_OK) {
String device_id = String(V_BSTR(&did));
@@ -5869,7 +5867,7 @@ DisplayServerWindows::~DisplayServerWindows() {
#endif
if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) {
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
- windows[MAIN_WINDOW_ID].wtctx = 0;
+ windows[MAIN_WINDOW_ID].wtctx = nullptr;
}
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
@@ -5887,7 +5885,7 @@ DisplayServerWindows::~DisplayServerWindows() {
#endif
if (restore_mouse_trails > 1) {
- SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, 0, 0);
+ SystemParametersInfoA(SPI_SETMOUSETRAILS, restore_mouse_trails, nullptr, 0);
}
#ifdef GLES3_ENABLED
if (gl_manager_angle) {
diff --git a/platform/windows/gl_manager_windows_native.cpp b/platform/windows/gl_manager_windows_native.cpp
index 8af32395b7..da837b3f94 100644
--- a/platform/windows/gl_manager_windows_native.cpp
+++ b/platform/windows/gl_manager_windows_native.cpp
@@ -107,22 +107,22 @@ static bool nvapi_err_check(const char *msg, int status) {
// to avoid stuttering, see https://stackoverflow.com/questions/36959508/nvidia-graphics-driver-causing-noticeable-frame-stuttering/37632948
// also see https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
- HMODULE nvapi = 0;
+ HMODULE nvapi = nullptr;
#ifdef _WIN64
nvapi = LoadLibraryA("nvapi64.dll");
#else
nvapi = LoadLibraryA("nvapi.dll");
#endif
- if (nvapi == NULL) {
+ if (nvapi == nullptr) {
return;
}
- void *(__cdecl * NvAPI_QueryInterface)(unsigned int interface_id) = 0;
+ void *(__cdecl * NvAPI_QueryInterface)(unsigned int interface_id) = nullptr;
NvAPI_QueryInterface = (void *(__cdecl *)(unsigned int))(void *)GetProcAddress(nvapi, "nvapi_QueryInterface");
- if (NvAPI_QueryInterface == NULL) {
+ if (NvAPI_QueryInterface == nullptr) {
print_verbose("Error getting NVAPI NvAPI_QueryInterface");
return;
}
@@ -176,7 +176,7 @@ void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
Char16String app_executable_name_u16 = app_executable_name.utf16();
Char16String app_friendly_name_u16 = app_friendly_name.utf16();
- NvDRSProfileHandle profile_handle = 0;
+ NvDRSProfileHandle profile_handle = nullptr;
int profile_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle);
@@ -195,7 +195,7 @@ void GLManagerNative_Windows::_nvapi_disable_threaded_optimization() {
}
}
- NvDRSProfileHandle app_profile_handle = 0;
+ NvDRSProfileHandle app_profile_handle = nullptr;
NVDRS_APPLICATION_V4 app;
app.version = NVDRS_APPLICATION_VER_V4;
@@ -362,14 +362,14 @@ Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_disp
if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported
{
gd_wglDeleteContext(gl_display.hRC);
- gl_display.hRC = 0;
+ gl_display.hRC = nullptr;
return ERR_CANT_CREATE;
}
- HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, 0, attribs);
+ HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, nullptr, attribs);
if (!new_hRC) {
gd_wglDeleteContext(gl_display.hRC);
- gl_display.hRC = 0;
+ gl_display.hRC = nullptr;
return ERR_CANT_CREATE;
}
@@ -384,7 +384,7 @@ Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_disp
{
ERR_PRINT("Could not attach OpenGL context to newly created window with replaced OpenGL context: " + format_error_message(GetLastError()));
gd_wglDeleteContext(gl_display.hRC);
- gl_display.hRC = 0;
+ gl_display.hRC = nullptr;
return ERR_CANT_CREATE;
}
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 93d1ffeac1..152b9ac10f 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -329,7 +329,7 @@ void debug_dynamic_library_check_dependencies(const String &p_root_path, const S
if (import_desc) {
for (; import_desc->Name && import_desc->FirstThunk; import_desc++) {
char16_t full_name_wc[MAX_PATH];
- const char *name_cs = (const char *)ImageRvaToVa(loaded_image.FileHeader, loaded_image.MappedAddress, import_desc->Name, 0);
+ const char *name_cs = (const char *)ImageRvaToVa(loaded_image.FileHeader, loaded_image.MappedAddress, import_desc->Name, nullptr);
String name = String(name_cs);
if (name.begins_with("api-ms-win-")) {
r_checked.insert(name);
@@ -463,9 +463,9 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
REFCLSID clsid = CLSID_WbemLocator; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator; // Interface UUID
- IWbemLocator *wbemLocator = NULL; // to get the services
- IWbemServices *wbemServices = NULL; // to get the class
- IEnumWbemClassObject *iter = NULL;
+ IWbemLocator *wbemLocator = nullptr; // to get the services
+ IWbemServices *wbemServices = nullptr; // to get the class
+ IEnumWbemClassObject *iter = nullptr;
IWbemClassObject *pnpSDriverObject[1]; // contains driver name, version, etc.
String driver_name;
String driver_version;
@@ -475,12 +475,12 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
return Vector<String>();
}
- HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
+ HRESULT hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, uuid, (LPVOID *)&wbemLocator);
if (hr != S_OK) {
return Vector<String>();
}
BSTR resource_name = SysAllocString(L"root\\CIMV2");
- hr = wbemLocator->ConnectServer(resource_name, NULL, NULL, NULL, 0, NULL, NULL, &wbemServices);
+ hr = wbemLocator->ConnectServer(resource_name, nullptr, nullptr, nullptr, 0, nullptr, nullptr, &wbemServices);
SysFreeString(resource_name);
SAFE_RELEASE(wbemLocator) // from now on, use `wbemServices`
@@ -492,7 +492,7 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
const String gpu_device_class_query = vformat("SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \"%s\"", device_name);
BSTR query = SysAllocString((const WCHAR *)gpu_device_class_query.utf16().get_data());
BSTR query_lang = SysAllocString(L"WQL");
- hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &iter);
+ hr = wbemServices->ExecQuery(query_lang, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, nullptr, &iter);
SysFreeString(query_lang);
SysFreeString(query);
if (hr == S_OK) {
@@ -504,13 +504,13 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
VariantInit(&dn);
BSTR object_name = SysAllocString(L"DriverName");
- hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL);
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, nullptr, nullptr);
SysFreeString(object_name);
if (hr == S_OK) {
String d_name = String(V_BSTR(&dn));
if (d_name.is_empty()) {
object_name = SysAllocString(L"DriverProviderName");
- hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL);
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, nullptr, nullptr);
SysFreeString(object_name);
if (hr == S_OK) {
driver_name = String(V_BSTR(&dn));
@@ -520,7 +520,7 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
}
} else {
object_name = SysAllocString(L"DriverProviderName");
- hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, NULL, NULL);
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &dn, nullptr, nullptr);
SysFreeString(object_name);
if (hr == S_OK) {
driver_name = String(V_BSTR(&dn));
@@ -530,7 +530,7 @@ Vector<String> OS_Windows::get_video_adapter_driver_info() const {
VARIANT dv;
VariantInit(&dv);
object_name = SysAllocString(L"DriverVersion");
- hr = pnpSDriverObject[0]->Get(object_name, 0, &dv, NULL, NULL);
+ hr = pnpSDriverObject[0]->Get(object_name, 0, &dv, nullptr, nullptr);
SysFreeString(object_name);
if (hr == S_OK) {
driver_version = String(V_BSTR(&dv));
@@ -783,7 +783,7 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
DWORD read = 0;
for (;;) { // Read StdOut and StdErr from pipe.
bytes.resize(bytes_in_buffer + CHUNK_SIZE);
- const bool success = ReadFile(pipe[0], bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, NULL);
+ const bool success = ReadFile(pipe[0], bytes.ptr() + bytes_in_buffer, CHUNK_SIZE, &read, nullptr);
if (!success || read == 0) {
break;
}
@@ -873,7 +873,7 @@ Error OS_Windows::kill(const ProcessID &p_pid) {
CloseHandle(pi.hThread);
} else {
HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, false, (DWORD)p_pid);
- if (hProcess != NULL) {
+ if (hProcess != nullptr) {
ret = TerminateProcess(hProcess, 0);
CloseHandle(hProcess);
@@ -1455,7 +1455,7 @@ String OS_Windows::get_processor_name() const {
WCHAR buffer[256];
DWORD buffer_len = 256;
DWORD vtype = REG_SZ;
- if (RegQueryValueExW(hkey, L"ProcessorNameString", NULL, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS) {
+ if (RegQueryValueExW(hkey, L"ProcessorNameString", nullptr, &vtype, (LPBYTE)buffer, &buffer_len) == ERROR_SUCCESS) {
RegCloseKey(hkey);
return String::utf16((const char16_t *)buffer, buffer_len).strip_edges();
} else {
diff --git a/platform/windows/tts_windows.cpp b/platform/windows/tts_windows.cpp
index 11d63d85ee..39a8f3e120 100644
--- a/platform/windows/tts_windows.cpp
+++ b/platform/windows/tts_windows.cpp
@@ -35,7 +35,7 @@ TTS_Windows *TTS_Windows::singleton = nullptr;
void __stdcall TTS_Windows::speech_event_callback(WPARAM wParam, LPARAM lParam) {
TTS_Windows *tts = TTS_Windows::get_singleton();
SPEVENT event;
- while (tts->synth->GetEvents(1, &event, NULL) == S_OK) {
+ while (tts->synth->GetEvents(1, &event, nullptr) == S_OK) {
uint32_t stream_num = (uint32_t)event.ulStreamNum;
if (tts->ids.has(stream_num)) {
if (event.eEventId == SPEI_START_INPUT_STREAM) {
@@ -82,7 +82,7 @@ void TTS_Windows::_update_tts() {
if (SUCCEEDED(hr)) {
hr = cpEnum->GetCount(&ulCount);
while (SUCCEEDED(hr) && ulCount--) {
- wchar_t *w_id = 0L;
+ wchar_t *w_id = nullptr;
hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
cpVoiceToken->GetId(&w_id);
if (String::utf16((const char16_t *)w_id) == message.voice) {
diff --git a/platform/windows/wgl_detect_version.cpp b/platform/windows/wgl_detect_version.cpp
index 49da4b58c7..12dd6f6ee6 100644
--- a/platform/windows/wgl_detect_version.cpp
+++ b/platform/windows/wgl_detect_version.cpp
@@ -140,7 +140,7 @@ Dictionary detect_wgl() {
PFNWGLCREATECONTEXTATTRIBSARBPROC gd_wglCreateContextAttribsARB = nullptr;
gd_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)gd_wglGetProcAddress("wglCreateContextAttribsARB");
if (gd_wglCreateContextAttribsARB) {
- HGLRC new_hRC = gd_wglCreateContextAttribsARB(hDC, 0, attribs);
+ HGLRC new_hRC = gd_wglCreateContextAttribsARB(hDC, nullptr, attribs);
if (new_hRC) {
if (gd_wglMakeCurrent(hDC, new_hRC)) {
PFNWGLGETSTRINGPROC gd_wglGetString = (PFNWGLGETSTRINGPROC)GetProcAddress(module, "glGetString");
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index f2efa7fcba..bc8e0b9015 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1417,6 +1417,10 @@ static const char32_t _oem_to_unicode[][129] = {
};
Error FontFile::load_bitmap_font(const String &p_path) {
+ return _load_bitmap_font(p_path, nullptr);
+}
+
+Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_files) {
reset_state();
antialiasing = TextServer::FONT_ANTIALIASING_NONE;
@@ -1558,6 +1562,9 @@ Error FontFile::load_bitmap_font(const String &p_path) {
img.instantiate();
Error err = ImageLoader::load_image(file, img);
ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat("Can't load font texture: %s.", file));
+ if (r_image_files) {
+ r_image_files->push_back(file);
+ }
if (packed) {
if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
@@ -1849,6 +1856,10 @@ Error FontFile::load_bitmap_font(const String &p_path) {
img.instantiate();
Error err = ImageLoader::load_image(file, img);
ERR_FAIL_COND_V_MSG(err != OK, ERR_FILE_CANT_READ, vformat("Can't load font texture: %s.", file));
+ if (r_image_files) {
+ r_image_files->push_back(file);
+ }
+
if (packed) {
if (ch[3] == 0) { // 4 x 8 bit monochrome, no outline
outline = 0;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index a435d14a02..1878539a3f 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -227,6 +227,8 @@ protected:
virtual void reset_state() override;
public:
+ Error _load_bitmap_font(const String &p_path, List<String> *r_image_files);
+
Error load_bitmap_font(const String &p_path);
Error load_dynamic_font(const String &p_path);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 44c7d2e0d9..322a8a853e 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -2443,8 +2443,8 @@ bool BaseMaterial3D::is_proximity_fade_enabled() const {
}
void BaseMaterial3D::set_proximity_fade_distance(float p_distance) {
- proximity_fade_distance = p_distance;
- RS::get_singleton()->material_set_param(_get_material(), shader_names->proximity_fade_distance, p_distance);
+ proximity_fade_distance = MAX(p_distance, 0.01);
+ RS::get_singleton()->material_set_param(_get_material(), shader_names->proximity_fade_distance, proximity_fade_distance);
}
float BaseMaterial3D::get_proximity_fade_distance() const {
@@ -2894,7 +2894,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "use_particle_trails"), "set_flag", "get_flag", FLAG_PARTICLE_TRAILS_MODE);
ADD_GROUP("Proximity Fade", "proximity_fade_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "proximity_fade_enabled"), "set_proximity_fade_enabled", "is_proximity_fade_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0,4096,0.01,suffix:m"), "set_proximity_fade_distance", "get_proximity_fade_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "proximity_fade_distance", PROPERTY_HINT_RANGE, "0.01,4096,0.01,suffix:m"), "set_proximity_fade_distance", "get_proximity_fade_distance");
ADD_GROUP("MSDF", "msdf_");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "msdf_outline_size", PROPERTY_HINT_RANGE, "0,250,1"), "set_msdf_outline_size", "get_msdf_outline_size");
diff --git a/servers/SCsub b/servers/SCsub
index 3e83657335..2e4430f030 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -15,22 +15,24 @@ env.add_source_files(env.servers_sources, "physics_server_2d_wrap_mt.cpp")
env.add_source_files(env.servers_sources, "register_server_types.cpp")
env.add_source_files(env.servers_sources, "rendering_server.cpp")
env.add_source_files(env.servers_sources, "text_server.cpp")
-env.add_source_files(env.servers_sources, "xr_server.cpp")
-SConscript("xr/SCsub")
-SConscript("camera/SCsub")
-if not env["disable_3d"]:
- SConscript("physics_3d/SCsub")
- env.add_source_files(env.servers_sources, "physics_server_3d.cpp")
- env.add_source_files(env.servers_sources, "physics_server_3d_wrap_mt.cpp")
-SConscript("physics_2d/SCsub")
-SConscript("rendering/SCsub")
SConscript("audio/SCsub")
-SConscript("text/SCsub")
+SConscript("camera/SCsub")
SConscript("debugger/SCsub")
SConscript("extensions/SCsub")
SConscript("movie_writer/SCsub")
SConscript("navigation/SCsub")
+SConscript("rendering/SCsub")
+SConscript("text/SCsub")
+
+SConscript("physics_2d/SCsub")
+
+if not env["disable_3d"]:
+ SConscript("physics_3d/SCsub")
+ env.add_source_files(env.servers_sources, "physics_server_3d.cpp")
+ env.add_source_files(env.servers_sources, "physics_server_3d_wrap_mt.cpp")
+ SConscript("xr/SCsub")
+ env.add_source_files(env.servers_sources, "xr_server.cpp")
lib = env.add_library("servers", env.servers_sources)
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 8547ebfad6..4e49129941 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -77,13 +77,6 @@
#include "text/text_server_dummy.h"
#include "text/text_server_extension.h"
#include "text_server.h"
-#include "xr/xr_body_tracker.h"
-#include "xr/xr_face_tracker.h"
-#include "xr/xr_hand_tracker.h"
-#include "xr/xr_interface.h"
-#include "xr/xr_interface_extension.h"
-#include "xr/xr_positional_tracker.h"
-#include "xr_server.h"
// 2D physics and navigation.
#include "navigation_server_2d.h"
@@ -99,6 +92,13 @@
#include "physics_server_3d.h"
#include "physics_server_3d_wrap_mt.h"
#include "servers/extensions/physics_server_3d_extension.h"
+#include "xr/xr_body_tracker.h"
+#include "xr/xr_face_tracker.h"
+#include "xr/xr_hand_tracker.h"
+#include "xr/xr_interface.h"
+#include "xr/xr_interface_extension.h"
+#include "xr/xr_positional_tracker.h"
+#include "xr_server.h"
#endif // _3D_DISABLED
ShaderTypes *shader_types = nullptr;
@@ -165,26 +165,10 @@ void register_server_types() {
GDREGISTER_CLASS(NativeMenu);
- GDREGISTER_ABSTRACT_CLASS(NavigationServer2D);
- GDREGISTER_ABSTRACT_CLASS(NavigationServer3D);
- GDREGISTER_CLASS(NavigationPathQueryParameters2D);
- GDREGISTER_CLASS(NavigationPathQueryParameters3D);
- GDREGISTER_CLASS(NavigationPathQueryResult2D);
- GDREGISTER_CLASS(NavigationPathQueryResult3D);
-
- GDREGISTER_CLASS(XRServer);
GDREGISTER_CLASS(CameraServer);
GDREGISTER_ABSTRACT_CLASS(RenderingDevice);
- GDREGISTER_CLASS(XRBodyTracker);
- GDREGISTER_ABSTRACT_CLASS(XRInterface);
- GDREGISTER_CLASS(XRHandTracker);
- GDREGISTER_CLASS(XRInterfaceExtension); // can't register this as virtual because we need a creation function for our extensions.
- GDREGISTER_CLASS(XRPose);
- GDREGISTER_CLASS(XRPositionalTracker);
- GDREGISTER_CLASS(XRFaceTracker);
-
GDREGISTER_CLASS(AudioStream);
GDREGISTER_CLASS(AudioStreamPlayback);
GDREGISTER_VIRTUAL_CLASS(AudioStreamPlaybackResampled);
@@ -303,6 +287,10 @@ void register_server_types() {
PhysicsServer2DManager::get_singleton()->register_server("GodotPhysics2D", callable_mp_static(_createGodotPhysics2DCallback));
PhysicsServer2DManager::get_singleton()->set_default_server("GodotPhysics2D");
+ GDREGISTER_ABSTRACT_CLASS(NavigationServer2D);
+ GDREGISTER_CLASS(NavigationPathQueryParameters2D);
+ GDREGISTER_CLASS(NavigationPathQueryResult2D);
+
#ifndef _3D_DISABLED
// Physics 3D
GDREGISTER_CLASS(PhysicsServer3DManager);
@@ -332,8 +320,21 @@ void register_server_types() {
PhysicsServer3DManager::get_singleton()->register_server("GodotPhysics3D", callable_mp_static(_createGodotPhysics3DCallback));
PhysicsServer3DManager::get_singleton()->set_default_server("GodotPhysics3D");
+
+ GDREGISTER_ABSTRACT_CLASS(XRInterface);
+ GDREGISTER_CLASS(XRBodyTracker);
+ GDREGISTER_CLASS(XRFaceTracker);
+ GDREGISTER_CLASS(XRHandTracker);
+ GDREGISTER_CLASS(XRInterfaceExtension); // can't register this as virtual because we need a creation function for our extensions.
+ GDREGISTER_CLASS(XRPose);
+ GDREGISTER_CLASS(XRPositionalTracker);
+ GDREGISTER_CLASS(XRServer);
#endif // _3D_DISABLED
+ GDREGISTER_ABSTRACT_CLASS(NavigationServer3D);
+ GDREGISTER_CLASS(NavigationPathQueryParameters3D);
+ GDREGISTER_CLASS(NavigationPathQueryResult3D);
+
writer_mjpeg = memnew(MovieWriterMJPEG);
MovieWriter::add_writer(writer_mjpeg);
@@ -357,18 +358,19 @@ void unregister_server_types() {
void register_server_singletons() {
OS::get_singleton()->benchmark_begin_measure("Servers", "Register Singletons");
+ Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer"));
Engine::get_singleton()->add_singleton(Engine::Singleton("DisplayServer", DisplayServer::get_singleton(), "DisplayServer"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NativeMenu", NativeMenu::get_singleton(), "NativeMenu"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton(), "NavigationServer2D"));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("RenderingServer", RenderingServer::get_singleton(), "RenderingServer"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("AudioServer", AudioServer::get_singleton(), "AudioServer"));
+
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer2D", PhysicsServer2D::get_singleton(), "PhysicsServer2D"));
#ifndef _3D_DISABLED
Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton(), "PhysicsServer3D"));
-#endif // _3D_DISABLED
- Engine::get_singleton()->add_singleton(Engine::Singleton("NativeMenu", NativeMenu::get_singleton(), "NativeMenu"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton(), "NavigationServer2D"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton(), "NavigationServer3D"));
Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton(), "XRServer"));
- Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton(), "CameraServer"));
+#endif // _3D_DISABLED
OS::get_singleton()->benchmark_end_measure("Servers", "Register Singletons");
}
diff --git a/servers/rendering/renderer_compositor.cpp b/servers/rendering/renderer_compositor.cpp
index c36da51e50..d364de5633 100644
--- a/servers/rendering/renderer_compositor.cpp
+++ b/servers/rendering/renderer_compositor.cpp
@@ -49,9 +49,11 @@ bool RendererCompositor::is_xr_enabled() const {
RendererCompositor::RendererCompositor() {
singleton = this;
+#ifndef _3D_DISABLED
if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
xr_enabled = GLOBAL_GET("xr/shaders/enabled");
} else {
xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON;
}
+#endif // _3D_DISABLED
}
diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp
index ce1ac2e929..30c318fb9a 100644
--- a/servers/rendering/renderer_rd/effects/vrs.cpp
+++ b/servers/rendering/renderer_rd/effects/vrs.cpp
@@ -32,7 +32,10 @@
#include "../renderer_compositor_rd.h"
#include "../storage_rd/texture_storage.h"
#include "../uniform_set_cache_rd.h"
+
+#ifndef _3D_DISABLED
#include "servers/xr_server.h"
+#endif // _3D_DISABLED
using namespace RendererRD;
@@ -124,6 +127,7 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
copy_vrs(rd_texture, p_vrs_fb, layers > 1);
}
}
+#ifndef _3D_DISABLED
} else if (vrs_mode == RS::VIEWPORT_VRS_XR) {
Ref<XRInterface> interface = XRServer::get_singleton()->get_primary_interface();
if (interface.is_valid()) {
@@ -138,6 +142,7 @@ void VRS::update_vrs_texture(RID p_vrs_fb, RID p_render_target) {
}
}
}
+#endif // _3D_DISABLED
}
RD::get_singleton()->draw_command_end_label();
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 86090aa416..341ba0e3b0 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -44,7 +44,10 @@
#include "servers/rendering/rendering_method.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering/storage/utilities.h"
+
+#ifndef _3D_DISABLED
#include "servers/xr/xr_interface.h"
+#endif // _3D_DISABLED
class RenderingLightCuller;
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 445a1b265e..baa198626c 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -220,6 +220,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
}
void RendererViewport::_draw_3d(Viewport *p_viewport) {
+#ifndef _3D_DISABLED
RENDER_TIMESTAMP("> Render 3D Scene");
Ref<XRInterface> xr_interface;
@@ -246,6 +247,7 @@ void RendererViewport::_draw_3d(Viewport *p_viewport) {
RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->self, p_viewport->internal_size, p_viewport->jitter_phase_count, screen_mesh_lod_threshold, p_viewport->shadow_atlas, xr_interface, &p_viewport->render_info);
RENDER_TIMESTAMP("< Render 3D Scene");
+#endif // _3D_DISABLED
}
void RendererViewport::_draw_viewport(Viewport *p_viewport) {
@@ -629,6 +631,7 @@ void RendererViewport::_draw_viewport(Viewport *p_viewport) {
void RendererViewport::draw_viewports(bool p_swap_buffers) {
timestamp_vp_map.clear();
+#ifndef _3D_DISABLED
// get our xr interface in case we need it
Ref<XRInterface> xr_interface;
XRServer *xr_server = XRServer::get_singleton();
@@ -639,6 +642,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
// retrieve the interface responsible for rendering
xr_interface = xr_server->get_primary_interface();
}
+#endif // _3D_DISABLED
if (Engine::get_singleton()->is_editor_hint()) {
set_default_clear_color(GLOBAL_GET("rendering/environment/defaults/default_clear_color"));
@@ -671,6 +675,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
bool visible = vp->viewport_to_screen_rect != Rect2();
+#ifndef _3D_DISABLED
if (vp->use_xr) {
if (xr_interface.is_valid()) {
// Ignore update mode we have to commit frames to our XR interface
@@ -684,7 +689,9 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
visible = false;
vp->size = Size2();
}
- } else {
+ } else
+#endif // _3D_DISABLED
+ {
if (vp->update_mode == RS::VIEWPORT_UPDATE_ALWAYS || vp->update_mode == RS::VIEWPORT_UPDATE_ONCE) {
visible = true;
}
@@ -722,6 +729,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
RENDER_TIMESTAMP("> Render Viewport " + itos(i));
RSG::texture_storage->render_target_set_as_unused(vp->render_target);
+#ifndef _3D_DISABLED
if (vp->use_xr && xr_interface.is_valid()) {
// Inform XR interface we're about to render its viewport,
// if this returns false we don't render.
@@ -758,7 +766,9 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) {
RSG::rasterizer->end_viewport(p_swap_buffers && blits.size() > 0);
}
}
- } else {
+ } else
+#endif // _3D_DISABLED
+ {
RSG::texture_storage->render_target_set_override(vp->render_target, RID(), RID(), RID());
RSG::scene->set_debug_draw_mode(vp->debug_draw);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index a0ec9e6318..90c29618b3 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -37,9 +37,12 @@
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering/rendering_method.h"
#include "servers/rendering_server.h"
-#include "servers/xr/xr_interface.h"
#include "storage/render_scene_buffers.h"
+#ifndef _3D_DISABLED
+#include "servers/xr/xr_interface.h"
+#endif // _3D_DISABLED
+
class RendererViewport {
public:
struct CanvasBase {
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index 4569846752..aa5e7d83cc 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -33,7 +33,14 @@
#include "servers/rendering/storage/render_scene_buffers.h"
#include "servers/rendering_server.h"
+
+#ifdef _3D_DISABLED
+// RendererSceneCull::render_camera is empty when 3D is disabled, but
+// it and RenderingMethod::render_camera have a parameter for XRInterface.
+#define XRInterface RefCounted
+#else // 3D enabled
#include "servers/xr/xr_interface.h"
+#endif // _3D_DISABLED
class RenderingMethod {
public:
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index bf8ab27722..b4c539dcff 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -93,11 +93,13 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) {
RSG::rasterizer->end_frame(p_swap_buffers);
+#ifndef _3D_DISABLED
XRServer *xr_server = XRServer::get_singleton();
if (xr_server != nullptr) {
// let our XR server know we're done so we can get our frame timing
xr_server->end_frame();
}
+#endif // _3D_DISABLED
RSG::canvas->update_visibility_notifiers();
RSG::scene->update_visibility_notifiers();
diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h
index 77b6ecf319..a5ab444f55 100644
--- a/servers/text/text_server_dummy.h
+++ b/servers/text/text_server_dummy.h
@@ -40,9 +40,87 @@ class TextServerDummy : public TextServerExtension {
_THREAD_SAFE_CLASS_
public:
- virtual String get_name() const override {
- return "Dummy";
- }
+ virtual bool has_feature(Feature p_feature) const override { return false; }
+ virtual String get_name() const override { return "Dummy"; }
+ virtual int64_t get_features() const override { return 0; }
+ virtual void free_rid(const RID &p_rid) override {}
+ virtual bool has(const RID &p_rid) override { return false; }
+
+ virtual RID create_font() override { return RID(); }
+ virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) override {}
+ virtual int64_t font_get_fixed_size(const RID &p_font_rid) const override { return 0; }
+ virtual void font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) override {}
+ virtual TextServer::FixedSizeScaleMode font_get_fixed_size_scale_mode(const RID &p_font_rid) const override { return FIXED_SIZE_SCALE_DISABLE; }
+ virtual TypedArray<Vector2i> font_get_size_cache_list(const RID &p_font_rid) const override { return TypedArray<Vector2i>(); }
+ virtual void font_clear_size_cache(const RID &p_font_rid) override {}
+ virtual void font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) override {}
+ virtual void font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) override {}
+ virtual double font_get_ascent(const RID &p_font_rid, int64_t p_size) const override { return 0; }
+ virtual void font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) override {}
+ virtual double font_get_descent(const RID &p_font_rid, int64_t p_size) const override { return 0; }
+ virtual void font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) override {}
+ virtual double font_get_underline_position(const RID &p_font_rid, int64_t p_size) const override { return 0; }
+ virtual void font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) override {}
+ virtual double font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const override { return 0; }
+ virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override {}
+ virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override { return 0; }
+ virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override { return 0; }
+ virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override {}
+ virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override {}
+ virtual void font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) override {}
+ virtual Ref<Image> font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override { return Ref<Image>(); }
+ virtual PackedInt32Array font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const override { return PackedInt32Array(); }
+ virtual void font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) override {}
+ virtual void font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) override {}
+ virtual Vector2 font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const override { return Vector2(); }
+ virtual void font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) override {}
+ virtual Vector2 font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return Vector2(); }
+ virtual void font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) override {}
+ virtual Vector2 font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return Vector2(); }
+ virtual void font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) override {}
+ virtual Rect2 font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return Rect2(); }
+ virtual void font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) override {}
+ virtual int64_t font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return 0; }
+ virtual void font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) override {}
+ virtual RID font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return RID(); }
+ virtual Size2 font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override { return Size2(); }
+ virtual int64_t font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const override { return 0; }
+ virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override { return 0; }
+ virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override { return false; }
+ virtual String font_get_supported_chars(const RID &p_font_rid) const override { return String(); }
+ virtual void font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
+ virtual void font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
+
+ virtual RID create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) override { return RID(); }
+ virtual void shaped_text_clear(const RID &p_shaped) override {}
+ virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) override { return false; }
+ virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, double p_baseline) override { return false; }
+ virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) override { return false; }
+ virtual int64_t shaped_get_span_count(const RID &p_shaped) const override { return 0; }
+ virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override { return Variant(); }
+ virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) override {}
+ virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override { return RID(); }
+ virtual RID shaped_text_get_parent(const RID &p_shaped) const override { return RID(); }
+ virtual bool shaped_text_shape(const RID &p_shaped) override { return false; }
+ virtual bool shaped_text_is_ready(const RID &p_shaped) const override { return false; }
+ virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const override { return nullptr; }
+ virtual const Glyph *shaped_text_sort_logical(const RID &p_shaped) override { return nullptr; }
+ virtual int64_t shaped_text_get_glyph_count(const RID &p_shaped) const override { return 0; }
+ virtual Vector2i shaped_text_get_range(const RID &p_shaped) const override { return Vector2i(); }
+ virtual int64_t shaped_text_get_trim_pos(const RID &p_shaped) const override { return -1; }
+ virtual int64_t shaped_text_get_ellipsis_pos(const RID &p_shaped) const override { return -1; }
+ virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override { return nullptr; }
+ virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override { return -1; }
+ virtual Array shaped_text_get_objects(const RID &p_shaped) const override { return Array(); }
+ virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override { return Rect2(); }
+ virtual Vector2i shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const override { return Vector2i(); }
+ virtual int64_t shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const override { return -1; }
+ virtual Size2 shaped_text_get_size(const RID &p_shaped) const override { return Size2(); }
+ virtual double shaped_text_get_ascent(const RID &p_shaped) const override { return 0; }
+ virtual double shaped_text_get_descent(const RID &p_shaped) const override { return 0; }
+ virtual double shaped_text_get_width(const RID &p_shaped) const override { return 0; }
+ virtual double shaped_text_get_underline_position(const RID &p_shaped) const override { return 0; }
+ virtual double shaped_text_get_underline_thickness(const RID &p_shaped) const override { return 0; }
};
#endif // TEXT_SERVER_DUMMY_H
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 113972d20d..eb9f1c1484 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -326,8 +326,8 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_prev_character_pos, "shaped", "pos");
GDVIRTUAL_BIND(_shaped_text_closest_character_pos, "shaped", "pos");
- GDVIRTUAL_BIND(_format_number, "string", "language");
- GDVIRTUAL_BIND(_parse_number, "string", "language");
+ GDVIRTUAL_BIND(_format_number, "number", "language");
+ GDVIRTUAL_BIND(_parse_number, "number", "language");
GDVIRTUAL_BIND(_percent_sign, "language");
GDVIRTUAL_BIND(_strip_diacritics, "string");
@@ -349,29 +349,29 @@ void TextServerExtension::_bind_methods() {
bool TextServerExtension::has_feature(Feature p_feature) const {
bool ret = false;
- GDVIRTUAL_CALL(_has_feature, p_feature, ret);
+ GDVIRTUAL_REQUIRED_CALL(_has_feature, p_feature, ret);
return ret;
}
String TextServerExtension::get_name() const {
String ret = "Unknown";
- GDVIRTUAL_CALL(_get_name, ret);
+ GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
return ret;
}
int64_t TextServerExtension::get_features() const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_get_features, ret);
+ GDVIRTUAL_REQUIRED_CALL(_get_features, ret);
return ret;
}
void TextServerExtension::free_rid(const RID &p_rid) {
- GDVIRTUAL_CALL(_free_rid, p_rid);
+ GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
}
bool TextServerExtension::has(const RID &p_rid) {
bool ret = false;
- GDVIRTUAL_CALL(_has, p_rid, ret);
+ GDVIRTUAL_REQUIRED_CALL(_has, p_rid, ret);
return ret;
}
@@ -407,14 +407,18 @@ bool TextServerExtension::is_locale_right_to_left(const String &p_locale) const
int64_t TextServerExtension::name_to_tag(const String &p_name) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_name_to_tag, p_name, ret);
- return ret;
+ if (GDVIRTUAL_CALL(_name_to_tag, p_name, ret)) {
+ return ret;
+ }
+ return TextServer::name_to_tag(p_name);
}
String TextServerExtension::tag_to_name(int64_t p_tag) const {
String ret;
- GDVIRTUAL_CALL(_tag_to_name, p_tag, ret);
- return ret;
+ if (GDVIRTUAL_CALL(_tag_to_name, p_tag, ret)) {
+ return ret;
+ }
+ return TextServer::tag_to_name(p_tag);
}
/*************************************************************************/
@@ -423,7 +427,7 @@ String TextServerExtension::tag_to_name(int64_t p_tag) const {
RID TextServerExtension::create_font() {
RID ret;
- GDVIRTUAL_CALL(_create_font, ret);
+ GDVIRTUAL_REQUIRED_CALL(_create_font, ret);
return ret;
}
@@ -452,7 +456,7 @@ int64_t TextServerExtension::font_get_face_index(const RID &p_font_rid) const {
}
int64_t TextServerExtension::font_get_face_count(const RID &p_font_rid) const {
- int64_t ret = 0;
+ int64_t ret = 1;
GDVIRTUAL_CALL(_font_get_face_count, p_font_rid, ret);
return ret;
}
@@ -574,22 +578,22 @@ int64_t TextServerExtension::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerExtension::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- GDVIRTUAL_CALL(_font_set_fixed_size, p_font_rid, p_fixed_size);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_fixed_size, p_font_rid, p_fixed_size);
}
int64_t TextServerExtension::font_get_fixed_size(const RID &p_font_rid) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_font_get_fixed_size, p_font_rid, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_fixed_size, p_font_rid, ret);
return ret;
}
void TextServerExtension::font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
- GDVIRTUAL_CALL(_font_set_fixed_size_scale_mode, p_font_rid, p_fixed_size_scale_mode);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_fixed_size_scale_mode, p_font_rid, p_fixed_size_scale_mode);
}
TextServer::FixedSizeScaleMode TextServerExtension::font_get_fixed_size_scale_mode(const RID &p_font_rid) const {
FixedSizeScaleMode ret = FIXED_SIZE_SCALE_DISABLE;
- GDVIRTUAL_CALL(_font_get_fixed_size_scale_mode, p_font_rid, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_fixed_size_scale_mode, p_font_rid, ret);
return ret;
}
@@ -695,89 +699,89 @@ double TextServerExtension::font_get_oversampling(const RID &p_font_rid) const {
TypedArray<Vector2i> TextServerExtension::font_get_size_cache_list(const RID &p_font_rid) const {
TypedArray<Vector2i> ret;
- GDVIRTUAL_CALL(_font_get_size_cache_list, p_font_rid, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_size_cache_list, p_font_rid, ret);
return ret;
}
void TextServerExtension::font_clear_size_cache(const RID &p_font_rid) {
- GDVIRTUAL_CALL(_font_clear_size_cache, p_font_rid);
+ GDVIRTUAL_REQUIRED_CALL(_font_clear_size_cache, p_font_rid);
}
void TextServerExtension::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_CALL(_font_remove_size_cache, p_font_rid, p_size);
+ GDVIRTUAL_REQUIRED_CALL(_font_remove_size_cache, p_font_rid, p_size);
}
void TextServerExtension::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- GDVIRTUAL_CALL(_font_set_ascent, p_font_rid, p_size, p_ascent);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_ascent, p_font_rid, p_size, p_ascent);
}
double TextServerExtension::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_CALL(_font_get_ascent, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_ascent, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- GDVIRTUAL_CALL(_font_set_descent, p_font_rid, p_size, p_descent);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_descent, p_font_rid, p_size, p_descent);
}
double TextServerExtension::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_CALL(_font_get_descent, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_descent, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- GDVIRTUAL_CALL(_font_set_underline_position, p_font_rid, p_size, p_underline_position);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_underline_position, p_font_rid, p_size, p_underline_position);
}
double TextServerExtension::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_CALL(_font_get_underline_position, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_underline_position, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- GDVIRTUAL_CALL(_font_set_underline_thickness, p_font_rid, p_size, p_underline_thickness);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_underline_thickness, p_font_rid, p_size, p_underline_thickness);
}
double TextServerExtension::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_CALL(_font_get_underline_thickness, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_underline_thickness, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- GDVIRTUAL_CALL(_font_set_scale, p_font_rid, p_size, p_scale);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_scale, p_font_rid, p_size, p_scale);
}
double TextServerExtension::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_CALL(_font_get_scale, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_scale, p_font_rid, p_size, ret);
return ret;
}
int64_t TextServerExtension::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_font_get_texture_count, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_texture_count, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_CALL(_font_clear_textures, p_font_rid, p_size);
+ GDVIRTUAL_REQUIRED_CALL(_font_clear_textures, p_font_rid, p_size);
}
void TextServerExtension::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- GDVIRTUAL_CALL(_font_remove_texture, p_font_rid, p_size, p_texture_index);
+ GDVIRTUAL_REQUIRED_CALL(_font_remove_texture, p_font_rid, p_size, p_texture_index);
}
void TextServerExtension::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- GDVIRTUAL_CALL(_font_set_texture_image, p_font_rid, p_size, p_texture_index, p_image);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_texture_image, p_font_rid, p_size, p_texture_index, p_image);
}
Ref<Image> TextServerExtension::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
Ref<Image> ret;
- GDVIRTUAL_CALL(_font_get_texture_image, p_font_rid, p_size, p_texture_index, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_texture_image, p_font_rid, p_size, p_texture_index, ret);
return ret;
}
@@ -793,77 +797,77 @@ PackedInt32Array TextServerExtension::font_get_texture_offsets(const RID &p_font
PackedInt32Array TextServerExtension::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
PackedInt32Array ret;
- GDVIRTUAL_CALL(_font_get_glyph_list, p_font_rid, p_size, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_list, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_CALL(_font_clear_glyphs, p_font_rid, p_size);
+ GDVIRTUAL_REQUIRED_CALL(_font_clear_glyphs, p_font_rid, p_size);
}
void TextServerExtension::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- GDVIRTUAL_CALL(_font_remove_glyph, p_font_rid, p_size, p_glyph);
+ GDVIRTUAL_REQUIRED_CALL(_font_remove_glyph, p_font_rid, p_size, p_glyph);
}
Vector2 TextServerExtension::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_CALL(_font_get_glyph_advance, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_advance, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- GDVIRTUAL_CALL(_font_set_glyph_advance, p_font_rid, p_size, p_glyph, p_advance);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_advance, p_font_rid, p_size, p_glyph, p_advance);
}
Vector2 TextServerExtension::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_CALL(_font_get_glyph_offset, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_offset, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- GDVIRTUAL_CALL(_font_set_glyph_offset, p_font_rid, p_size, p_glyph, p_offset);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_offset, p_font_rid, p_size, p_glyph, p_offset);
}
Vector2 TextServerExtension::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_CALL(_font_get_glyph_size, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_size, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- GDVIRTUAL_CALL(_font_set_glyph_size, p_font_rid, p_size, p_glyph, p_gl_size);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_size, p_font_rid, p_size, p_glyph, p_gl_size);
}
Rect2 TextServerExtension::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Rect2 ret;
- GDVIRTUAL_CALL(_font_get_glyph_uv_rect, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_uv_rect, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- GDVIRTUAL_CALL(_font_set_glyph_uv_rect, p_font_rid, p_size, p_glyph, p_uv_rect);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_uv_rect, p_font_rid, p_size, p_glyph, p_uv_rect);
}
int64_t TextServerExtension::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_font_get_glyph_texture_idx, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_idx, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- GDVIRTUAL_CALL(_font_set_glyph_texture_idx, p_font_rid, p_size, p_glyph, p_texture_idx);
+ GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_texture_idx, p_font_rid, p_size, p_glyph, p_texture_idx);
}
RID TextServerExtension::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
RID ret;
- GDVIRTUAL_CALL(_font_get_glyph_texture_rid, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_rid, p_font_rid, p_size, p_glyph, ret);
return ret;
}
Size2 TextServerExtension::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Size2 ret;
- GDVIRTUAL_CALL(_font_get_glyph_texture_size, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_size, p_font_rid, p_size, p_glyph, ret);
return ret;
}
@@ -899,25 +903,25 @@ Vector2 TextServerExtension::font_get_kerning(const RID &p_font_rid, int64_t p_s
int64_t TextServerExtension::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_font_get_glyph_index, p_font_rid, p_size, p_char, p_variation_selector, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_index, p_font_rid, p_size, p_char, p_variation_selector, ret);
return ret;
}
int64_t TextServerExtension::font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_font_get_char_from_glyph_index, p_font_rid, p_size, p_glyph_index, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_char_from_glyph_index, p_font_rid, p_size, p_glyph_index, ret);
return ret;
}
bool TextServerExtension::font_has_char(const RID &p_font_rid, int64_t p_char) const {
bool ret = false;
- GDVIRTUAL_CALL(_font_has_char, p_font_rid, p_char, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_has_char, p_font_rid, p_char, ret);
return ret;
}
String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) const {
String ret;
- GDVIRTUAL_CALL(_font_get_supported_chars, p_font_rid, ret);
+ GDVIRTUAL_REQUIRED_CALL(_font_get_supported_chars, p_font_rid, ret);
return ret;
}
@@ -930,11 +934,11 @@ void TextServerExtension::font_render_glyph(const RID &p_font_rid, const Vector2
}
void TextServerExtension::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- GDVIRTUAL_CALL(_font_draw_glyph, p_font_rid, p_canvas, p_size, p_pos, p_index, p_color);
+ GDVIRTUAL_REQUIRED_CALL(_font_draw_glyph, p_font_rid, p_canvas, p_size, p_pos, p_index, p_color);
}
void TextServerExtension::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- GDVIRTUAL_CALL(_font_draw_glyph_outline, p_font_rid, p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
+ GDVIRTUAL_REQUIRED_CALL(_font_draw_glyph_outline, p_font_rid, p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
}
bool TextServerExtension::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
@@ -1041,12 +1045,12 @@ void TextServerExtension::draw_hex_code_box(const RID &p_canvas, int64_t p_size,
RID TextServerExtension::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
RID ret;
- GDVIRTUAL_CALL(_create_shaped_text, p_direction, p_orientation, ret);
+ GDVIRTUAL_REQUIRED_CALL(_create_shaped_text, p_direction, p_orientation, ret);
return ret;
}
void TextServerExtension::shaped_text_clear(const RID &p_shaped) {
- GDVIRTUAL_CALL(_shaped_text_clear, p_shaped);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_clear, p_shaped);
}
void TextServerExtension::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
@@ -1131,47 +1135,47 @@ int64_t TextServerExtension::shaped_text_get_spacing(const RID &p_shaped, TextSe
bool TextServerExtension::shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
bool ret = false;
- GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, p_fonts, p_size, p_opentype_features, p_language, p_meta, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_add_string, p_shaped, p_text, p_fonts, p_size, p_opentype_features, p_language, p_meta, ret);
return ret;
}
bool TextServerExtension::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, double p_baseline) {
bool ret = false;
- GDVIRTUAL_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, p_baseline, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, p_baseline, ret);
return ret;
}
bool TextServerExtension::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
bool ret = false;
- GDVIRTUAL_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, p_baseline, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, p_baseline, ret);
return ret;
}
int64_t TextServerExtension::shaped_get_span_count(const RID &p_shaped) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_shaped_get_span_count, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_get_span_count, p_shaped, ret);
return ret;
}
Variant TextServerExtension::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
Variant ret = false;
- GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_get_span_meta, p_shaped, p_index, ret);
return ret;
}
void TextServerExtension::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
- GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features);
}
RID TextServerExtension::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
RID ret;
- GDVIRTUAL_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret);
return ret;
}
RID TextServerExtension::shaped_text_get_parent(const RID &p_shaped) const {
RID ret;
- GDVIRTUAL_CALL(_shaped_text_get_parent, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_parent, p_shaped, ret);
return ret;
}
@@ -1189,7 +1193,7 @@ double TextServerExtension::shaped_text_tab_align(const RID &p_shaped, const Pac
bool TextServerExtension::shaped_text_shape(const RID &p_shaped) {
bool ret = false;
- GDVIRTUAL_CALL(_shaped_text_shape, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_shape, p_shaped, ret);
return ret;
}
@@ -1207,31 +1211,31 @@ bool TextServerExtension::shaped_text_update_justification_ops(const RID &p_shap
bool TextServerExtension::shaped_text_is_ready(const RID &p_shaped) const {
bool ret = false;
- GDVIRTUAL_CALL(_shaped_text_is_ready, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_is_ready, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_get_glyphs(const RID &p_shaped) const {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_CALL(_shaped_text_get_glyphs, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_glyphs, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_sort_logical(const RID &p_shaped) {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_CALL(_shaped_text_sort_logical, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_sort_logical, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_glyph_count(const RID &p_shaped) const {
int64_t ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_glyph_count, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_glyph_count, p_shaped, ret);
return ret;
}
Vector2i TextServerExtension::shaped_text_get_range(const RID &p_shaped) const {
Vector2i ret;
- GDVIRTUAL_CALL(_shaped_text_get_range, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_range, p_shaped, ret);
return ret;
}
@@ -1261,25 +1265,25 @@ PackedInt32Array TextServerExtension::shaped_text_get_word_breaks(const RID &p_s
int64_t TextServerExtension::shaped_text_get_trim_pos(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_CALL(_shaped_text_get_trim_pos, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_trim_pos, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_CALL(_shaped_text_get_ellipsis_pos, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_pos, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyph_count, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_glyph_count, p_shaped, ret);
return ret;
}
@@ -1289,61 +1293,61 @@ void TextServerExtension::shaped_text_overrun_trim_to_width(const RID &p_shaped_
Array TextServerExtension::shaped_text_get_objects(const RID &p_shaped) const {
Array ret;
- GDVIRTUAL_CALL(_shaped_text_get_objects, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_objects, p_shaped, ret);
return ret;
}
Rect2 TextServerExtension::shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
Rect2 ret;
- GDVIRTUAL_CALL(_shaped_text_get_object_rect, p_shaped, p_key, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_rect, p_shaped, p_key, ret);
return ret;
}
Vector2i TextServerExtension::shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const {
Vector2i ret;
- GDVIRTUAL_CALL(_shaped_text_get_object_range, p_shaped, p_key, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_range, p_shaped, p_key, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const {
int64_t ret = -1;
- GDVIRTUAL_CALL(_shaped_text_get_object_glyph, p_shaped, p_key, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_glyph, p_shaped, p_key, ret);
return ret;
}
Size2 TextServerExtension::shaped_text_get_size(const RID &p_shaped) const {
Size2 ret;
- GDVIRTUAL_CALL(_shaped_text_get_size, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_size, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_ascent(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_ascent, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ascent, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_descent(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_descent, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_descent, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_width(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_width, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_width, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_underline_position(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_underline_position, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_underline_position, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_underline_thickness(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_CALL(_shaped_text_get_underline_thickness, p_shaped, ret);
+ GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_underline_thickness, p_shaped, ret);
return ret;
}
@@ -1513,8 +1517,10 @@ String TextServerExtension::string_to_lower(const String &p_string, const String
TypedArray<Vector3i> TextServerExtension::parse_structured_text(StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const {
TypedArray<Vector3i> ret;
- GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret);
- return ret;
+ if (GDVIRTUAL_CALL(_parse_structured_text, p_parser_type, p_args, p_text, ret)) {
+ return ret;
+ }
+ return TextServer::parse_structured_text(p_parser_type, p_args, p_text);
}
PackedInt32Array TextServerExtension::string_get_word_breaks(const String &p_string, const String &p_language, int64_t p_chars_per_line) const {
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index 64c1a2d7dd..078ee27753 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -642,6 +642,48 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(FIXED_SIZE_SCALE_ENABLED);
}
+_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
+ char tag[4];
+ uint32_t i;
+
+ if (!p_str || !p_len || !*p_str) {
+ return OT_TAG(0, 0, 0, 0);
+ }
+
+ if (p_len < 0 || p_len > 4) {
+ p_len = 4;
+ }
+ for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) {
+ tag[i] = p_str[i];
+ }
+
+ for (; i < 4; i++) {
+ tag[i] = ' ';
+ }
+
+ return OT_TAG(tag[0], tag[1], tag[2], tag[3]);
+}
+
+int64_t TextServer::name_to_tag(const String &p_name) const {
+ // No readable name, use tag string.
+ return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
+}
+
+_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {
+ p_buf[0] = (char)(uint8_t)(p_tag >> 24);
+ p_buf[1] = (char)(uint8_t)(p_tag >> 16);
+ p_buf[2] = (char)(uint8_t)(p_tag >> 8);
+ p_buf[3] = (char)(uint8_t)(p_tag >> 0);
+}
+
+String TextServer::tag_to_name(int64_t p_tag) const {
+ // No readable name, use tag string.
+ char name[5];
+ memset(name, 0, 5);
+ ot_tag_to_string(p_tag, name);
+ return String("custom_") + String(name);
+}
+
Vector2 TextServer::get_hex_code_box_size(int64_t p_size, int64_t p_index) const {
int w = ((p_index <= 0xFF) ? 1 : ((p_index <= 0xFFFF) ? 2 : 3));
int sp = MAX(0, w - 1);
diff --git a/servers/text_server.h b/servers/text_server.h
index d16a0e0066..4a16ae64e8 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -43,6 +43,8 @@ class TypedArray;
struct Glyph;
struct CaretInfo;
+#define OT_TAG(m_c1, m_c2, m_c3, m_c4) ((int32_t)((((uint32_t)(m_c1) & 0xff) << 24) | (((uint32_t)(m_c2) & 0xff) << 16) | (((uint32_t)(m_c3) & 0xff) << 8) | ((uint32_t)(m_c4) & 0xff)))
+
class TextServer : public RefCounted {
GDCLASS(TextServer, RefCounted);
@@ -239,8 +241,8 @@ public:
virtual bool is_locale_right_to_left(const String &p_locale) const = 0;
- virtual int64_t name_to_tag(const String &p_name) const { return 0; };
- virtual String tag_to_name(int64_t p_tag) const { return ""; };
+ virtual int64_t name_to_tag(const String &p_name) const;
+ virtual String tag_to_name(int64_t p_tag) const;
/* Font interface */
diff --git a/thirdparty/README.md b/thirdparty/README.md
index a36a8f209f..2c9bc36545 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -390,7 +390,7 @@ Files extracted from upstream source:
## icu4c
- Upstream: https://github.com/unicode-org/icu
-- Version: 74.1 (9edac7b78327a1cb58db29e2714b15f9fa14e4d7, 2023)
+- Version: 74.2 (2d029329c82c7792b985024b2bdab5fc7278fbc8, 2023)
- License: Unicode
Files extracted from upstream source:
@@ -690,7 +690,7 @@ Collection of single-file libraries used in Godot components.
## msdfgen
- Upstream: https://github.com/Chlumsky/msdfgen
-- Version: 1.10 (64a91eec3ca3787e6f78b4c99fcd3052ad3e37c0, 2021)
+- Version: 1.11 (f12d7ca00091a632a289865b85c3f2e0bfc6542d, 2023)
- License: MIT
Files extracted from the upstream source:
diff --git a/thirdparty/icu4c/common/unicode/uvernum.h b/thirdparty/icu4c/common/unicode/uvernum.h
index 9fc98e7380..1cdf8912f9 100644
--- a/thirdparty/icu4c/common/unicode/uvernum.h
+++ b/thirdparty/icu4c/common/unicode/uvernum.h
@@ -59,7 +59,7 @@
* This value will change in the subsequent releases of ICU
* @stable ICU 2.6
*/
-#define U_ICU_VERSION_MINOR_NUM 1
+#define U_ICU_VERSION_MINOR_NUM 2
/** The current ICU patchlevel version as an integer.
* This value will change in the subsequent releases of ICU
@@ -132,7 +132,7 @@
* This value will change in the subsequent releases of ICU
* @stable ICU 2.4
*/
-#define U_ICU_VERSION "74.1"
+#define U_ICU_VERSION "74.2"
/**
* The current ICU library major version number as a string, for library name suffixes.
@@ -151,7 +151,7 @@
/** Data version in ICU4C.
* @internal ICU 4.4 Internal Use Only
**/
-#define U_ICU_DATA_VERSION "74.1"
+#define U_ICU_DATA_VERSION "74.2"
#endif /* U_HIDE_INTERNAL_API */
/*===========================================================================
diff --git a/thirdparty/icu4c/icudt74l.dat b/thirdparty/icu4c/icudt74l.dat
index 8576067fd0..e4ce73e23c 100644
--- a/thirdparty/icu4c/icudt74l.dat
+++ b/thirdparty/icu4c/icudt74l.dat
Binary files differ
diff --git a/thirdparty/msdfgen/LICENSE.txt b/thirdparty/msdfgen/LICENSE.txt
index 9757001664..69054bd2e1 100644
--- a/thirdparty/msdfgen/LICENSE.txt
+++ b/thirdparty/msdfgen/LICENSE.txt
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2016 - 2022 Viktor Chlumsky
+Copyright (c) 2016 - 2023 Viktor Chlumsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/msdfgen/core/Bitmap.h b/thirdparty/msdfgen/core/Bitmap.h
index 14407d6c34..9a08749f47 100644
--- a/thirdparty/msdfgen/core/Bitmap.h
+++ b/thirdparty/msdfgen/core/Bitmap.h
@@ -18,17 +18,17 @@ public:
Bitmap(Bitmap<T, N> &&orig);
#endif
~Bitmap();
- Bitmap<T, N> & operator=(const BitmapConstRef<T, N> &orig);
- Bitmap<T, N> & operator=(const Bitmap<T, N> &orig);
+ Bitmap<T, N> &operator=(const BitmapConstRef<T, N> &orig);
+ Bitmap<T, N> &operator=(const Bitmap<T, N> &orig);
#ifdef MSDFGEN_USE_CPP11
- Bitmap<T, N> & operator=(Bitmap<T, N> &&orig);
+ Bitmap<T, N> &operator=(Bitmap<T, N> &&orig);
#endif
/// Bitmap width in pixels.
int width() const;
/// Bitmap height in pixels.
int height() const;
- T * operator()(int x, int y);
- const T * operator()(int x, int y) const;
+ T *operator()(int x, int y);
+ const T *operator()(int x, int y) const;
#ifdef MSDFGEN_USE_CPP11
explicit operator T *();
explicit operator const T *() const;
diff --git a/thirdparty/msdfgen/core/Bitmap.hpp b/thirdparty/msdfgen/core/Bitmap.hpp
index cb16cac8d4..9404357788 100644
--- a/thirdparty/msdfgen/core/Bitmap.hpp
+++ b/thirdparty/msdfgen/core/Bitmap.hpp
@@ -40,7 +40,7 @@ Bitmap<T, N>::~Bitmap() {
}
template <typename T, int N>
-Bitmap<T, N> & Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
+Bitmap<T, N> &Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
if (pixels != orig.pixels) {
delete [] pixels;
w = orig.width, h = orig.height;
@@ -51,7 +51,7 @@ Bitmap<T, N> & Bitmap<T, N>::operator=(const BitmapConstRef<T, N> &orig) {
}
template <typename T, int N>
-Bitmap<T, N> & Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
+Bitmap<T, N> &Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
if (this != &orig) {
delete [] pixels;
w = orig.w, h = orig.h;
@@ -63,7 +63,7 @@ Bitmap<T, N> & Bitmap<T, N>::operator=(const Bitmap<T, N> &orig) {
#ifdef MSDFGEN_USE_CPP11
template <typename T, int N>
-Bitmap<T, N> & Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
+Bitmap<T, N> &Bitmap<T, N>::operator=(Bitmap<T, N> &&orig) {
if (this != &orig) {
delete [] pixels;
pixels = orig.pixels;
@@ -85,12 +85,12 @@ int Bitmap<T, N>::height() const {
}
template <typename T, int N>
-T * Bitmap<T, N>::operator()(int x, int y) {
+T *Bitmap<T, N>::operator()(int x, int y) {
return pixels+N*(w*y+x);
}
template <typename T, int N>
-const T * Bitmap<T, N>::operator()(int x, int y) const {
+const T *Bitmap<T, N>::operator()(int x, int y) const {
return pixels+N*(w*y+x);
}
diff --git a/thirdparty/msdfgen/core/BitmapRef.hpp b/thirdparty/msdfgen/core/BitmapRef.hpp
index 6f9620dcdf..cb17f95dcb 100644
--- a/thirdparty/msdfgen/core/BitmapRef.hpp
+++ b/thirdparty/msdfgen/core/BitmapRef.hpp
@@ -1,12 +1,10 @@
#pragma once
-#include <cstdlib>
+#include "base.h"
namespace msdfgen {
-typedef unsigned char byte;
-
/// Reference to a 2D image bitmap or a buffer acting as one. Pixel storage not owned or managed by the object.
template <typename T, int N = 1>
struct BitmapRef {
@@ -17,7 +15,7 @@ struct BitmapRef {
inline BitmapRef() : pixels(NULL), width(0), height(0) { }
inline BitmapRef(T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
- inline T * operator()(int x, int y) const {
+ inline T *operator()(int x, int y) const {
return pixels+N*(width*y+x);
}
@@ -34,7 +32,7 @@ struct BitmapConstRef {
inline BitmapConstRef(const T *pixels, int width, int height) : pixels(pixels), width(width), height(height) { }
inline BitmapConstRef(const BitmapRef<T, N> &orig) : pixels(orig.pixels), width(orig.width), height(orig.height) { }
- inline const T * operator()(int x, int y) const {
+ inline const T *operator()(int x, int y) const {
return pixels+N*(width*y+x);
}
diff --git a/thirdparty/msdfgen/core/Contour.cpp b/thirdparty/msdfgen/core/Contour.cpp
index ca80d3c55a..57293dc58b 100644
--- a/thirdparty/msdfgen/core/Contour.cpp
+++ b/thirdparty/msdfgen/core/Contour.cpp
@@ -19,7 +19,7 @@ void Contour::addEdge(EdgeHolder &&edge) {
}
#endif
-EdgeHolder & Contour::addEdge() {
+EdgeHolder &Contour::addEdge() {
edges.resize(edges.size()+1);
return edges.back();
}
diff --git a/thirdparty/msdfgen/core/Contour.h b/thirdparty/msdfgen/core/Contour.h
index f79b269582..4cae48fad7 100644
--- a/thirdparty/msdfgen/core/Contour.h
+++ b/thirdparty/msdfgen/core/Contour.h
@@ -19,7 +19,7 @@ public:
void addEdge(EdgeHolder &&edge);
#endif
/// Creates a new edge in the contour and returns its reference.
- EdgeHolder & addEdge();
+ EdgeHolder &addEdge();
/// Adjusts the bounding box to fit the contour.
void bound(double &l, double &b, double &r, double &t) const;
/// Adjusts the bounding box to fit the contour border's mitered corners.
diff --git a/thirdparty/msdfgen/core/EdgeColor.h b/thirdparty/msdfgen/core/EdgeColor.h
index 9d49a5a89e..5d3730c9a6 100644
--- a/thirdparty/msdfgen/core/EdgeColor.h
+++ b/thirdparty/msdfgen/core/EdgeColor.h
@@ -1,6 +1,8 @@
#pragma once
+#include "base.h"
+
namespace msdfgen {
/// Edge color specifies which color channels an edge belongs to.
diff --git a/thirdparty/msdfgen/core/EdgeHolder.cpp b/thirdparty/msdfgen/core/EdgeHolder.cpp
index 1a8c5f66e9..cffcff46d3 100644
--- a/thirdparty/msdfgen/core/EdgeHolder.cpp
+++ b/thirdparty/msdfgen/core/EdgeHolder.cpp
@@ -31,7 +31,7 @@ EdgeHolder::~EdgeHolder() {
delete edgeSegment;
}
-EdgeHolder & EdgeHolder::operator=(const EdgeHolder &orig) {
+EdgeHolder &EdgeHolder::operator=(const EdgeHolder &orig) {
if (this != &orig) {
delete edgeSegment;
edgeSegment = orig.edgeSegment ? orig.edgeSegment->clone() : NULL;
@@ -40,7 +40,7 @@ EdgeHolder & EdgeHolder::operator=(const EdgeHolder &orig) {
}
#ifdef MSDFGEN_USE_CPP11
-EdgeHolder & EdgeHolder::operator=(EdgeHolder &&orig) {
+EdgeHolder &EdgeHolder::operator=(EdgeHolder &&orig) {
if (this != &orig) {
delete edgeSegment;
edgeSegment = orig.edgeSegment;
@@ -50,19 +50,19 @@ EdgeHolder & EdgeHolder::operator=(EdgeHolder &&orig) {
}
#endif
-EdgeSegment & EdgeHolder::operator*() {
+EdgeSegment &EdgeHolder::operator*() {
return *edgeSegment;
}
-const EdgeSegment & EdgeHolder::operator*() const {
+const EdgeSegment &EdgeHolder::operator*() const {
return *edgeSegment;
}
-EdgeSegment * EdgeHolder::operator->() {
+EdgeSegment *EdgeHolder::operator->() {
return edgeSegment;
}
-const EdgeSegment * EdgeHolder::operator->() const {
+const EdgeSegment *EdgeHolder::operator->() const {
return edgeSegment;
}
diff --git a/thirdparty/msdfgen/core/EdgeHolder.h b/thirdparty/msdfgen/core/EdgeHolder.h
index c4c5be7616..50a59b2189 100644
--- a/thirdparty/msdfgen/core/EdgeHolder.h
+++ b/thirdparty/msdfgen/core/EdgeHolder.h
@@ -22,14 +22,14 @@ public:
EdgeHolder(EdgeHolder &&orig);
#endif
~EdgeHolder();
- EdgeHolder & operator=(const EdgeHolder &orig);
+ EdgeHolder &operator=(const EdgeHolder &orig);
#ifdef MSDFGEN_USE_CPP11
- EdgeHolder & operator=(EdgeHolder &&orig);
+ EdgeHolder &operator=(EdgeHolder &&orig);
#endif
- EdgeSegment & operator*();
- const EdgeSegment & operator*() const;
- EdgeSegment * operator->();
- const EdgeSegment * operator->() const;
+ EdgeSegment &operator*();
+ const EdgeSegment &operator*() const;
+ EdgeSegment *operator->();
+ const EdgeSegment *operator->() const;
operator EdgeSegment *();
operator const EdgeSegment *() const;
diff --git a/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp b/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
index 7918597fd2..9a5cefe128 100644
--- a/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
+++ b/thirdparty/msdfgen/core/MSDFErrorCorrection.cpp
@@ -19,8 +19,8 @@ namespace msdfgen {
#define CLASSIFIER_FLAG_CANDIDATE 0x01
#define CLASSIFIER_FLAG_ARTIFACT 0x02
-const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
-const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
+MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinDeviationRatio = 1.11111111111111111;
+MSDFGEN_PUBLIC const double ErrorCorrectionConfig::defaultMinImproveRatio = 1.11111111111111111;
/// The base artifact classifier recognizes artifacts based on the contents of the SDF alone.
class BaseArtifactClassifier {
@@ -317,7 +317,7 @@ static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifie
em[0] = am, em[1] = dm;
tEnd[tEx0 > t[i]] = tEx0;
em[tEx0 > t[i]] = interpolatedMedian(a, l, q, tEx0);
- rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
}
// tEx1
if (tEx1 > 0 && tEx1 < 1) {
@@ -325,7 +325,7 @@ static bool hasDiagonalArtifactInner(const ArtifactClassifier &artifactClassifie
em[0] = am, em[1] = dm;
tEnd[tEx1 > t[i]] = tEx1;
em[tEx1 > t[i]] = interpolatedMedian(a, l, q, tEx1);
- rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], am, dm, xm);
+ rangeFlags |= artifactClassifier.rangeTest(tEnd[0], tEnd[1], t[i], em[0], em[1], xm);
}
if (artifactClassifier.evaluate(t[i], xm, rangeFlags))
return true;
diff --git a/thirdparty/msdfgen/core/Projection.h b/thirdparty/msdfgen/core/Projection.h
index 7cdb1c307a..3c457776ac 100644
--- a/thirdparty/msdfgen/core/Projection.h
+++ b/thirdparty/msdfgen/core/Projection.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Vector2.h"
+#include "Vector2.hpp"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/Scanline.cpp b/thirdparty/msdfgen/core/Scanline.cpp
index 8e5352dbf6..7407be41b5 100644
--- a/thirdparty/msdfgen/core/Scanline.cpp
+++ b/thirdparty/msdfgen/core/Scanline.cpp
@@ -1,7 +1,7 @@
#include "Scanline.h"
-#include <algorithm>
+#include <cstdlib>
#include "arithmetics.hpp"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/Scanline.h b/thirdparty/msdfgen/core/Scanline.h
index 9c8f34044b..c0abc68642 100644
--- a/thirdparty/msdfgen/core/Scanline.h
+++ b/thirdparty/msdfgen/core/Scanline.h
@@ -2,6 +2,7 @@
#pragma once
#include <vector>
+#include "base.h"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/Shape.cpp b/thirdparty/msdfgen/core/Shape.cpp
index 8d6f47c807..cf99bd0f0c 100644
--- a/thirdparty/msdfgen/core/Shape.cpp
+++ b/thirdparty/msdfgen/core/Shape.cpp
@@ -1,7 +1,7 @@
#include "Shape.h"
-#include <algorithm>
+#include <cstdlib>
#include "arithmetics.hpp"
namespace msdfgen {
@@ -18,7 +18,7 @@ void Shape::addContour(Contour &&contour) {
}
#endif
-Contour & Shape::addContour() {
+Contour &Shape::addContour() {
contours.resize(contours.size()+1);
return contours.back();
}
@@ -40,15 +40,12 @@ bool Shape::validate() const {
}
static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
- {
- const QuadraticSegment *quadraticSegment = dynamic_cast<const QuadraticSegment *>(&*edgeHolder);
- if (quadraticSegment)
- edgeHolder = quadraticSegment->convertToCubic();
- }
- {
- CubicSegment *cubicSegment = dynamic_cast<CubicSegment *>(&*edgeHolder);
- if (cubicSegment)
- cubicSegment->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
+ switch (edgeHolder->type()) {
+ case (int) QuadraticSegment::EDGE_TYPE:
+ edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic();
+ // fallthrough
+ case (int) CubicSegment::EDGE_TYPE:
+ static_cast<CubicSegment *>(&*edgeHolder)->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
}
}
@@ -162,16 +159,18 @@ void Shape::orientContours() {
}
}
}
- qsort(&intersections[0], intersections.size(), sizeof(Intersection), &Intersection::compare);
- // Disqualify multiple intersections
- for (int j = 1; j < (int) intersections.size(); ++j)
- if (intersections[j].x == intersections[j-1].x)
- intersections[j].direction = intersections[j-1].direction = 0;
- // Inspect scanline and deduce orientations of intersected contours
- for (int j = 0; j < (int) intersections.size(); ++j)
- if (intersections[j].direction)
- orientations[intersections[j].contourIndex] += 2*((j&1)^(intersections[j].direction > 0))-1;
- intersections.clear();
+ if (!intersections.empty()) {
+ qsort(&intersections[0], intersections.size(), sizeof(Intersection), &Intersection::compare);
+ // Disqualify multiple intersections
+ for (int j = 1; j < (int) intersections.size(); ++j)
+ if (intersections[j].x == intersections[j-1].x)
+ intersections[j].direction = intersections[j-1].direction = 0;
+ // Inspect scanline and deduce orientations of intersected contours
+ for (int j = 0; j < (int) intersections.size(); ++j)
+ if (intersections[j].direction)
+ orientations[intersections[j].contourIndex] += 2*((j&1)^(intersections[j].direction > 0))-1;
+ intersections.clear();
+ }
}
}
// Reverse contours that have the opposite orientation
diff --git a/thirdparty/msdfgen/core/Shape.h b/thirdparty/msdfgen/core/Shape.h
index 7539921ce7..fd9222dbc4 100644
--- a/thirdparty/msdfgen/core/Shape.h
+++ b/thirdparty/msdfgen/core/Shape.h
@@ -32,7 +32,7 @@ public:
void addContour(Contour &&contour);
#endif
/// Adds a blank contour and returns its reference.
- Contour & addContour();
+ Contour &addContour();
/// Normalizes the shape geometry for distance field generation.
void normalize();
/// Performs basic checks to determine if the object represents a valid shape.
diff --git a/thirdparty/msdfgen/core/ShapeDistanceFinder.h b/thirdparty/msdfgen/core/ShapeDistanceFinder.h
index 57df8d8e72..d8a584f645 100644
--- a/thirdparty/msdfgen/core/ShapeDistanceFinder.h
+++ b/thirdparty/msdfgen/core/ShapeDistanceFinder.h
@@ -2,7 +2,7 @@
#pragma once
#include <vector>
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "edge-selectors.h"
#include "contour-combiners.h"
diff --git a/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp b/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp
index 028738e5c3..07fb5cdc0e 100644
--- a/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp
+++ b/thirdparty/msdfgen/core/ShapeDistanceFinder.hpp
@@ -9,7 +9,11 @@ ShapeDistanceFinder<ContourCombiner>::ShapeDistanceFinder(const Shape &shape) :
template <class ContourCombiner>
typename ShapeDistanceFinder<ContourCombiner>::DistanceType ShapeDistanceFinder<ContourCombiner>::distance(const Point2 &origin) {
contourCombiner.reset(origin);
- typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = &shapeEdgeCache[0];
+#ifdef MSDFGEN_USE_CPP11
+ typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = shapeEdgeCache.data();
+#else
+ typename ContourCombiner::EdgeSelectorType::EdgeCache *edgeCache = shapeEdgeCache.empty() ? NULL : &shapeEdgeCache[0];
+#endif
for (std::vector<Contour>::const_iterator contour = shape.contours.begin(); contour != shape.contours.end(); ++contour) {
if (!contour->edges.empty()) {
diff --git a/thirdparty/msdfgen/core/SignedDistance.cpp b/thirdparty/msdfgen/core/SignedDistance.cpp
deleted file mode 100644
index be956d953e..0000000000
--- a/thirdparty/msdfgen/core/SignedDistance.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-
-#include "SignedDistance.h"
-
-#include <cmath>
-#include <cfloat>
-
-namespace msdfgen {
-
-SignedDistance::SignedDistance() : distance(-DBL_MAX), dot(1) { }
-
-SignedDistance::SignedDistance(double dist, double d) : distance(dist), dot(d) { }
-
-bool operator<(SignedDistance a, SignedDistance b) {
- return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot < b.dot);
-}
-
-bool operator>(SignedDistance a, SignedDistance b) {
- return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot > b.dot);
-}
-
-bool operator<=(SignedDistance a, SignedDistance b) {
- return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot <= b.dot);
-}
-
-bool operator>=(SignedDistance a, SignedDistance b) {
- return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot >= b.dot);
-}
-
-}
diff --git a/thirdparty/msdfgen/core/SignedDistance.h b/thirdparty/msdfgen/core/SignedDistance.h
deleted file mode 100644
index 91fee97600..0000000000
--- a/thirdparty/msdfgen/core/SignedDistance.h
+++ /dev/null
@@ -1,23 +0,0 @@
-
-#pragma once
-
-namespace msdfgen {
-
-/// Represents a signed distance and alignment, which together can be compared to uniquely determine the closest edge segment.
-class SignedDistance {
-
-public:
- double distance;
- double dot;
-
- SignedDistance();
- SignedDistance(double dist, double d);
-
- friend bool operator<(SignedDistance a, SignedDistance b);
- friend bool operator>(SignedDistance a, SignedDistance b);
- friend bool operator<=(SignedDistance a, SignedDistance b);
- friend bool operator>=(SignedDistance a, SignedDistance b);
-
-};
-
-}
diff --git a/thirdparty/msdfgen/core/SignedDistance.hpp b/thirdparty/msdfgen/core/SignedDistance.hpp
new file mode 100644
index 0000000000..62e73c03aa
--- /dev/null
+++ b/thirdparty/msdfgen/core/SignedDistance.hpp
@@ -0,0 +1,38 @@
+
+#pragma once
+
+#include <cmath>
+#include <cfloat>
+#include "base.h"
+
+namespace msdfgen {
+
+/// Represents a signed distance and alignment, which together can be compared to uniquely determine the closest edge segment.
+class SignedDistance {
+
+public:
+ double distance;
+ double dot;
+
+ inline SignedDistance() : distance(-DBL_MAX), dot(0) { }
+ inline SignedDistance(double dist, double d) : distance(dist), dot(d) { }
+
+};
+
+inline bool operator<(const SignedDistance a, const SignedDistance b) {
+ return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot < b.dot);
+}
+
+inline bool operator>(const SignedDistance a, const SignedDistance b) {
+ return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot > b.dot);
+}
+
+inline bool operator<=(const SignedDistance a, const SignedDistance b) {
+ return fabs(a.distance) < fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot <= b.dot);
+}
+
+inline bool operator>=(const SignedDistance a, const SignedDistance b) {
+ return fabs(a.distance) > fabs(b.distance) || (fabs(a.distance) == fabs(b.distance) && a.dot >= b.dot);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/Vector2.cpp b/thirdparty/msdfgen/core/Vector2.cpp
deleted file mode 100644
index 896963ff2c..0000000000
--- a/thirdparty/msdfgen/core/Vector2.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-
-#include "Vector2.h"
-
-namespace msdfgen {
-
-Vector2::Vector2(double val) : x(val), y(val) { }
-
-Vector2::Vector2(double x, double y) : x(x), y(y) { }
-
-void Vector2::reset() {
- x = 0, y = 0;
-}
-
-void Vector2::set(double x, double y) {
- Vector2::x = x, Vector2::y = y;
-}
-
-double Vector2::length() const {
- return sqrt(x*x+y*y);
-}
-
-double Vector2::direction() const {
- return atan2(y, x);
-}
-
-Vector2 Vector2::normalize(bool allowZero) const {
- double len = length();
- if (len == 0)
- return Vector2(0, !allowZero);
- return Vector2(x/len, y/len);
-}
-
-Vector2 Vector2::getOrthogonal(bool polarity) const {
- return polarity ? Vector2(-y, x) : Vector2(y, -x);
-}
-
-Vector2 Vector2::getOrthonormal(bool polarity, bool allowZero) const {
- double len = length();
- if (len == 0)
- return polarity ? Vector2(0, !allowZero) : Vector2(0, -!allowZero);
- return polarity ? Vector2(-y/len, x/len) : Vector2(y/len, -x/len);
-}
-
-Vector2 Vector2::project(const Vector2 &vector, bool positive) const {
- Vector2 n = normalize(true);
- double t = dotProduct(vector, n);
- if (positive && t <= 0)
- return Vector2();
- return t*n;
-}
-
-Vector2::operator const void*() const {
- return x || y ? this : NULL;
-}
-
-bool Vector2::operator!() const {
- return !x && !y;
-}
-
-bool Vector2::operator==(const Vector2 &other) const {
- return x == other.x && y == other.y;
-}
-
-bool Vector2::operator!=(const Vector2 &other) const {
- return x != other.x || y != other.y;
-}
-
-Vector2 Vector2::operator+() const {
- return *this;
-}
-
-Vector2 Vector2::operator-() const {
- return Vector2(-x, -y);
-}
-
-Vector2 Vector2::operator+(const Vector2 &other) const {
- return Vector2(x+other.x, y+other.y);
-}
-
-Vector2 Vector2::operator-(const Vector2 &other) const {
- return Vector2(x-other.x, y-other.y);
-}
-
-Vector2 Vector2::operator*(const Vector2 &other) const {
- return Vector2(x*other.x, y*other.y);
-}
-
-Vector2 Vector2::operator/(const Vector2 &other) const {
- return Vector2(x/other.x, y/other.y);
-}
-
-Vector2 Vector2::operator*(double value) const {
- return Vector2(x*value, y*value);
-}
-
-Vector2 Vector2::operator/(double value) const {
- return Vector2(x/value, y/value);
-}
-
-Vector2 & Vector2::operator+=(const Vector2 &other) {
- x += other.x, y += other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator-=(const Vector2 &other) {
- x -= other.x, y -= other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator*=(const Vector2 &other) {
- x *= other.x, y *= other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator/=(const Vector2 &other) {
- x /= other.x, y /= other.y;
- return *this;
-}
-
-Vector2 & Vector2::operator*=(double value) {
- x *= value, y *= value;
- return *this;
-}
-
-Vector2 & Vector2::operator/=(double value) {
- x /= value, y /= value;
- return *this;
-}
-
-double dotProduct(const Vector2 &a, const Vector2 &b) {
- return a.x*b.x+a.y*b.y;
-}
-
-double crossProduct(const Vector2 &a, const Vector2 &b) {
- return a.x*b.y-a.y*b.x;
-}
-
-Vector2 operator*(double value, const Vector2 &vector) {
- return Vector2(value*vector.x, value*vector.y);
-}
-
-Vector2 operator/(double value, const Vector2 &vector) {
- return Vector2(value/vector.x, value/vector.y);
-}
-
-}
diff --git a/thirdparty/msdfgen/core/Vector2.h b/thirdparty/msdfgen/core/Vector2.h
deleted file mode 100644
index 47ca637c3d..0000000000
--- a/thirdparty/msdfgen/core/Vector2.h
+++ /dev/null
@@ -1,66 +0,0 @@
-
-#pragma once
-
-#include <cstdlib>
-#include <cmath>
-
-namespace msdfgen {
-
-/**
-* A 2-dimensional euclidean vector with double precision.
-* Implementation based on the Vector2 template from Artery Engine.
-* @author Viktor Chlumsky
-*/
-struct Vector2 {
-
- double x, y;
-
- Vector2(double val = 0);
- Vector2(double x, double y);
- /// Sets the vector to zero.
- void reset();
- /// Sets individual elements of the vector.
- void set(double x, double y);
- /// Returns the vector's length.
- double length() const;
- /// Returns the angle of the vector in radians (atan2).
- double direction() const;
- /// Returns the normalized vector - one that has the same direction but unit length.
- Vector2 normalize(bool allowZero = false) const;
- /// Returns a vector with the same length that is orthogonal to this one.
- Vector2 getOrthogonal(bool polarity = true) const;
- /// Returns a vector with unit length that is orthogonal to this one.
- Vector2 getOrthonormal(bool polarity = true, bool allowZero = false) const;
- /// Returns a vector projected along this one.
- Vector2 project(const Vector2 &vector, bool positive = false) const;
- operator const void *() const;
- bool operator!() const;
- bool operator==(const Vector2 &other) const;
- bool operator!=(const Vector2 &other) const;
- Vector2 operator+() const;
- Vector2 operator-() const;
- Vector2 operator+(const Vector2 &other) const;
- Vector2 operator-(const Vector2 &other) const;
- Vector2 operator*(const Vector2 &other) const;
- Vector2 operator/(const Vector2 &other) const;
- Vector2 operator*(double value) const;
- Vector2 operator/(double value) const;
- Vector2 & operator+=(const Vector2 &other);
- Vector2 & operator-=(const Vector2 &other);
- Vector2 & operator*=(const Vector2 &other);
- Vector2 & operator/=(const Vector2 &other);
- Vector2 & operator*=(double value);
- Vector2 & operator/=(double value);
- /// Dot product of two vectors.
- friend double dotProduct(const Vector2 &a, const Vector2 &b);
- /// A special version of the cross product for 2D vectors (returns scalar value).
- friend double crossProduct(const Vector2 &a, const Vector2 &b);
- friend Vector2 operator*(double value, const Vector2 &vector);
- friend Vector2 operator/(double value, const Vector2 &vector);
-
-};
-
-/// A vector may also represent a point, which shall be differentiated semantically using the alias Point2.
-typedef Vector2 Point2;
-
-}
diff --git a/thirdparty/msdfgen/core/Vector2.hpp b/thirdparty/msdfgen/core/Vector2.hpp
new file mode 100644
index 0000000000..0208c61497
--- /dev/null
+++ b/thirdparty/msdfgen/core/Vector2.hpp
@@ -0,0 +1,167 @@
+
+#pragma once
+
+#include <cmath>
+#include "base.h"
+
+namespace msdfgen {
+
+/**
+ * A 2-dimensional euclidean floating-point vector.
+ * @author Viktor Chlumsky
+ */
+struct Vector2 {
+
+ double x, y;
+
+ inline Vector2(double val = 0) : x(val), y(val) { }
+
+ inline Vector2(double x, double y) : x(x), y(y) { }
+
+ /// Sets the vector to zero.
+ inline void reset() {
+ x = 0, y = 0;
+ }
+
+ /// Sets individual elements of the vector.
+ inline void set(double x, double y) {
+ this->x = x, this->y = y;
+ }
+
+ /// Returns the vector's squared length.
+ inline double squaredLength() const {
+ return x*x+y*y;
+ }
+
+ /// Returns the vector's length.
+ inline double length() const {
+ return sqrt(x*x+y*y);
+ }
+
+ /// Returns the normalized vector - one that has the same direction but unit length.
+ inline Vector2 normalize(bool allowZero = false) const {
+ if (double len = length())
+ return Vector2(x/len, y/len);
+ return Vector2(0, !allowZero);
+ }
+
+ /// Returns a vector with the same length that is orthogonal to this one.
+ inline Vector2 getOrthogonal(bool polarity = true) const {
+ return polarity ? Vector2(-y, x) : Vector2(y, -x);
+ }
+
+ /// Returns a vector with unit length that is orthogonal to this one.
+ inline Vector2 getOrthonormal(bool polarity = true, bool allowZero = false) const {
+ if (double len = length())
+ return polarity ? Vector2(-y/len, x/len) : Vector2(y/len, -x/len);
+ return polarity ? Vector2(0, !allowZero) : Vector2(0, -!allowZero);
+ }
+
+#ifdef MSDFGEN_USE_CPP11
+ inline explicit operator bool() const {
+ return x || y;
+ }
+#else
+ inline operator const void *() const {
+ return x || y ? this : NULL;
+ }
+#endif
+
+ inline Vector2 &operator+=(const Vector2 other) {
+ x += other.x, y += other.y;
+ return *this;
+ }
+
+ inline Vector2 &operator-=(const Vector2 other) {
+ x -= other.x, y -= other.y;
+ return *this;
+ }
+
+ inline Vector2 &operator*=(const Vector2 other) {
+ x *= other.x, y *= other.y;
+ return *this;
+ }
+
+ inline Vector2 &operator/=(const Vector2 other) {
+ x /= other.x, y /= other.y;
+ return *this;
+ }
+
+ inline Vector2 &operator*=(double value) {
+ x *= value, y *= value;
+ return *this;
+ }
+
+ inline Vector2 &operator/=(double value) {
+ x /= value, y /= value;
+ return *this;
+ }
+
+};
+
+/// A vector may also represent a point, which shall be differentiated semantically using the alias Point2.
+typedef Vector2 Point2;
+
+/// Dot product of two vectors.
+inline double dotProduct(const Vector2 a, const Vector2 b) {
+ return a.x*b.x+a.y*b.y;
+}
+
+/// A special version of the cross product for 2D vectors (returns scalar value).
+inline double crossProduct(const Vector2 a, const Vector2 b) {
+ return a.x*b.y-a.y*b.x;
+}
+
+inline bool operator==(const Vector2 a, const Vector2 b) {
+ return a.x == b.x && a.y == b.y;
+}
+
+inline bool operator!=(const Vector2 a, const Vector2 b) {
+ return a.x != b.x || a.y != b.y;
+}
+
+inline Vector2 operator+(const Vector2 v) {
+ return v;
+}
+
+inline Vector2 operator-(const Vector2 v) {
+ return Vector2(-v.x, -v.y);
+}
+
+inline bool operator!(const Vector2 v) {
+ return !v.x && !v.y;
+}
+
+inline Vector2 operator+(const Vector2 a, const Vector2 b) {
+ return Vector2(a.x+b.x, a.y+b.y);
+}
+
+inline Vector2 operator-(const Vector2 a, const Vector2 b) {
+ return Vector2(a.x-b.x, a.y-b.y);
+}
+
+inline Vector2 operator*(const Vector2 a, const Vector2 b) {
+ return Vector2(a.x*b.x, a.y*b.y);
+}
+
+inline Vector2 operator/(const Vector2 a, const Vector2 b) {
+ return Vector2(a.x/b.x, a.y/b.y);
+}
+
+inline Vector2 operator*(double a, const Vector2 b) {
+ return Vector2(a*b.x, a*b.y);
+}
+
+inline Vector2 operator/(double a, const Vector2 b) {
+ return Vector2(a/b.x, a/b.y);
+}
+
+inline Vector2 operator*(const Vector2 a, double b) {
+ return Vector2(a.x*b, a.y*b);
+}
+
+inline Vector2 operator/(const Vector2 a, double b) {
+ return Vector2(a.x/b, a.y/b);
+}
+
+}
diff --git a/thirdparty/msdfgen/core/arithmetics.hpp b/thirdparty/msdfgen/core/arithmetics.hpp
index 78c21d658e..d5158ad0d3 100644
--- a/thirdparty/msdfgen/core/arithmetics.hpp
+++ b/thirdparty/msdfgen/core/arithmetics.hpp
@@ -1,8 +1,8 @@
#pragma once
-#include <cstdlib>
#include <cmath>
+#include "base.h"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/base.h b/thirdparty/msdfgen/core/base.h
new file mode 100644
index 0000000000..ab85c136e1
--- /dev/null
+++ b/thirdparty/msdfgen/core/base.h
@@ -0,0 +1,16 @@
+
+#pragma once
+
+// This file needs to be included first for all MSDFgen sources
+
+#ifndef MSDFGEN_PUBLIC
+#include <msdfgen/msdfgen-config.h>
+#endif
+
+#include <cstddef>
+
+namespace msdfgen {
+
+typedef unsigned char byte;
+
+}
diff --git a/thirdparty/msdfgen/core/bitmap-interpolation.hpp b/thirdparty/msdfgen/core/bitmap-interpolation.hpp
index a14b0fb534..ca2e48c080 100644
--- a/thirdparty/msdfgen/core/bitmap-interpolation.hpp
+++ b/thirdparty/msdfgen/core/bitmap-interpolation.hpp
@@ -2,7 +2,7 @@
#pragma once
#include "arithmetics.hpp"
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "BitmapRef.hpp"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/contour-combiners.cpp b/thirdparty/msdfgen/core/contour-combiners.cpp
index 31f4c5da71..bac5534810 100644
--- a/thirdparty/msdfgen/core/contour-combiners.cpp
+++ b/thirdparty/msdfgen/core/contour-combiners.cpp
@@ -33,7 +33,7 @@ void SimpleContourCombiner<EdgeSelector>::reset(const Point2 &p) {
}
template <class EdgeSelector>
-EdgeSelector & SimpleContourCombiner<EdgeSelector>::edgeSelector(int) {
+EdgeSelector &SimpleContourCombiner<EdgeSelector>::edgeSelector(int) {
return shapeEdgeSelector;
}
@@ -63,7 +63,7 @@ void OverlappingContourCombiner<EdgeSelector>::reset(const Point2 &p) {
}
template <class EdgeSelector>
-EdgeSelector & OverlappingContourCombiner<EdgeSelector>::edgeSelector(int i) {
+EdgeSelector &OverlappingContourCombiner<EdgeSelector>::edgeSelector(int i) {
return edgeSelectors[i];
}
diff --git a/thirdparty/msdfgen/core/contour-combiners.h b/thirdparty/msdfgen/core/contour-combiners.h
index 944b119aba..04dbc34230 100644
--- a/thirdparty/msdfgen/core/contour-combiners.h
+++ b/thirdparty/msdfgen/core/contour-combiners.h
@@ -16,7 +16,7 @@ public:
explicit SimpleContourCombiner(const Shape &shape);
void reset(const Point2 &p);
- EdgeSelector & edgeSelector(int i);
+ EdgeSelector &edgeSelector(int i);
DistanceType distance() const;
private:
@@ -34,7 +34,7 @@ public:
explicit OverlappingContourCombiner(const Shape &shape);
void reset(const Point2 &p);
- EdgeSelector & edgeSelector(int i);
+ EdgeSelector &edgeSelector(int i);
DistanceType distance() const;
private:
diff --git a/thirdparty/msdfgen/core/edge-coloring.cpp b/thirdparty/msdfgen/core/edge-coloring.cpp
index 6a87bba54d..da9b037735 100644
--- a/thirdparty/msdfgen/core/edge-coloring.cpp
+++ b/thirdparty/msdfgen/core/edge-coloring.cpp
@@ -5,6 +5,7 @@
#include <cmath>
#include <cstring>
#include <cfloat>
+#include <vector>
#include <queue>
#include "arithmetics.hpp"
@@ -244,7 +245,7 @@ static double edgeToEdgeDistance(const EdgeSegment &a, const EdgeSegment &b, int
return minDistance;
}
-static double splineToSplineDistance(EdgeSegment * const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
+static double splineToSplineDistance(EdgeSegment *const *edgeSegments, int aStart, int aEnd, int bStart, int bEnd, int precision) {
double minDistance = DBL_MAX;
for (int ai = aStart; ai < aEnd; ++ai)
for (int bi = bStart; bi < bEnd && minDistance; ++bi) {
@@ -254,7 +255,7 @@ static double splineToSplineDistance(EdgeSegment * const *edgeSegments, int aSta
return minDistance;
}
-static void colorSecondDegreeGraph(int *coloring, const int * const *edgeMatrix, int vertexCount, unsigned long long seed) {
+static void colorSecondDegreeGraph(int *coloring, const int *const *edgeMatrix, int vertexCount, unsigned long long seed) {
for (int i = 0; i < vertexCount; ++i) {
int possibleColors = 7;
for (int j = 0; j < i; ++j) {
@@ -301,7 +302,7 @@ static int vertexPossibleColors(const int *coloring, const int *edgeVector, int
return 7&~usedColors;
}
-static void uncolorSameNeighbors(std::queue<int> &uncolored, int *coloring, const int * const *edgeMatrix, int vertex, int vertexCount) {
+static void uncolorSameNeighbors(std::queue<int> &uncolored, int *coloring, const int *const *edgeMatrix, int vertex, int vertexCount) {
for (int i = vertex+1; i < vertexCount; ++i) {
if (edgeMatrix[vertex][i] && coloring[i] == coloring[vertex]) {
coloring[i] = -1;
@@ -316,7 +317,7 @@ static void uncolorSameNeighbors(std::queue<int> &uncolored, int *coloring, cons
}
}
-static bool tryAddEdge(int *coloring, int * const *edgeMatrix, int vertexCount, int vertexA, int vertexB, int *coloringBuffer) {
+static bool tryAddEdge(int *coloring, int *const *edgeMatrix, int vertexCount, int vertexA, int vertexB, int *coloringBuffer) {
static const int FIRST_POSSIBLE_COLOR[8] = { -1, 0, 1, 0, 2, 2, 1, 0 };
edgeMatrix[vertexA][vertexB] = 1;
edgeMatrix[vertexB][vertexA] = 1;
@@ -358,7 +359,7 @@ static bool tryAddEdge(int *coloring, int * const *edgeMatrix, int vertexCount,
}
static int cmpDoublePtr(const void *a, const void *b) {
- return sign(**reinterpret_cast<const double * const *>(a)-**reinterpret_cast<const double * const *>(b));
+ return sign(**reinterpret_cast<const double *const *>(a)-**reinterpret_cast<const double *const *>(b));
}
void edgeColoringByDistance(Shape &shape, double angleThreshold, unsigned long long seed) {
diff --git a/thirdparty/msdfgen/core/edge-segments.cpp b/thirdparty/msdfgen/core/edge-segments.cpp
index 5274a9a5a1..4fef89b95b 100644
--- a/thirdparty/msdfgen/core/edge-segments.cpp
+++ b/thirdparty/msdfgen/core/edge-segments.cpp
@@ -56,18 +56,42 @@ CubicSegment::CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor
p[3] = p3;
}
-LinearSegment * LinearSegment::clone() const {
+LinearSegment *LinearSegment::clone() const {
return new LinearSegment(p[0], p[1], color);
}
-QuadraticSegment * QuadraticSegment::clone() const {
+QuadraticSegment *QuadraticSegment::clone() const {
return new QuadraticSegment(p[0], p[1], p[2], color);
}
-CubicSegment * CubicSegment::clone() const {
+CubicSegment *CubicSegment::clone() const {
return new CubicSegment(p[0], p[1], p[2], p[3], color);
}
+int LinearSegment::type() const {
+ return (int) EDGE_TYPE;
+}
+
+int QuadraticSegment::type() const {
+ return (int) EDGE_TYPE;
+}
+
+int CubicSegment::type() const {
+ return (int) EDGE_TYPE;
+}
+
+const Point2 *LinearSegment::controlPoints() const {
+ return p;
+}
+
+const Point2 *QuadraticSegment::controlPoints() const {
+ return p;
+}
+
+const Point2 *CubicSegment::controlPoints() const {
+ return p;
+}
+
Point2 LinearSegment::point(double param) const {
return mix(p[0], p[1], param);
}
@@ -483,7 +507,7 @@ void CubicSegment::splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeS
part3 = new CubicSegment(point(2/3.), mix(mix(p[1], p[2], 2/3.), mix(p[2], p[3], 2/3.), 2/3.), p[2] == p[3] ? p[3] : mix(p[2], p[3], 2/3.), p[3], color);
}
-EdgeSegment * QuadraticSegment::convertToCubic() const {
+EdgeSegment *QuadraticSegment::convertToCubic() const {
return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
}
diff --git a/thirdparty/msdfgen/core/edge-segments.h b/thirdparty/msdfgen/core/edge-segments.h
index 1c8fb599ff..71ce8fc4ab 100644
--- a/thirdparty/msdfgen/core/edge-segments.h
+++ b/thirdparty/msdfgen/core/edge-segments.h
@@ -1,8 +1,8 @@
#pragma once
-#include "Vector2.h"
-#include "SignedDistance.h"
+#include "Vector2.hpp"
+#include "SignedDistance.hpp"
#include "EdgeColor.h"
namespace msdfgen {
@@ -20,7 +20,11 @@ public:
EdgeSegment(EdgeColor edgeColor = WHITE) : color(edgeColor) { }
virtual ~EdgeSegment() { }
/// Creates a copy of the edge segment.
- virtual EdgeSegment * clone() const = 0;
+ virtual EdgeSegment *clone() const = 0;
+ /// Returns the numeric code of the edge segment's type.
+ virtual int type() const = 0;
+ /// Returns the array of control points.
+ virtual const Point2 *controlPoints() const = 0;
/// Returns the point on the edge specified by the parameter (between 0 and 1).
virtual Point2 point(double param) const = 0;
/// Returns the direction the edge has at the point specified by the parameter.
@@ -51,10 +55,16 @@ public:
class LinearSegment : public EdgeSegment {
public:
+ enum EdgeType {
+ EDGE_TYPE = 1
+ };
+
Point2 p[2];
LinearSegment(Point2 p0, Point2 p1, EdgeColor edgeColor = WHITE);
- LinearSegment * clone() const;
+ LinearSegment *clone() const;
+ int type() const;
+ const Point2 *controlPoints() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
@@ -74,10 +84,16 @@ public:
class QuadraticSegment : public EdgeSegment {
public:
+ enum EdgeType {
+ EDGE_TYPE = 2
+ };
+
Point2 p[3];
QuadraticSegment(Point2 p0, Point2 p1, Point2 p2, EdgeColor edgeColor = WHITE);
- QuadraticSegment * clone() const;
+ QuadraticSegment *clone() const;
+ int type() const;
+ const Point2 *controlPoints() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
@@ -91,7 +107,7 @@ public:
void moveEndPoint(Point2 to);
void splitInThirds(EdgeSegment *&part1, EdgeSegment *&part2, EdgeSegment *&part3) const;
- EdgeSegment * convertToCubic() const;
+ EdgeSegment *convertToCubic() const;
};
@@ -99,10 +115,16 @@ public:
class CubicSegment : public EdgeSegment {
public:
+ enum EdgeType {
+ EDGE_TYPE = 3
+ };
+
Point2 p[4];
CubicSegment(Point2 p0, Point2 p1, Point2 p2, Point2 p3, EdgeColor edgeColor = WHITE);
- CubicSegment * clone() const;
+ CubicSegment *clone() const;
+ int type() const;
+ const Point2 *controlPoints() const;
Point2 point(double param) const;
Vector2 direction(double param) const;
Vector2 directionChange(double param) const;
diff --git a/thirdparty/msdfgen/core/edge-selectors.h b/thirdparty/msdfgen/core/edge-selectors.h
index 3620999f82..6b8e2d943f 100644
--- a/thirdparty/msdfgen/core/edge-selectors.h
+++ b/thirdparty/msdfgen/core/edge-selectors.h
@@ -1,8 +1,8 @@
#pragma once
-#include "Vector2.h"
-#include "SignedDistance.h"
+#include "Vector2.hpp"
+#include "SignedDistance.hpp"
#include "edge-segments.h"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/equation-solver.cpp b/thirdparty/msdfgen/core/equation-solver.cpp
index 4144fa3340..7c1cbb39f8 100644
--- a/thirdparty/msdfgen/core/equation-solver.cpp
+++ b/thirdparty/msdfgen/core/equation-solver.cpp
@@ -49,7 +49,7 @@ static int solveCubicNormed(double x[3], double a, double b, double c) {
x[2] = q*cos(1/3.*(t-2*M_PI))-a;
return 3;
} else {
- double u = (r < 0 ? 1 : -1)*pow(fabs(r)+sqrt(r2-q3), 1/3.);
+ double u = (r < 0 ? 1 : -1)*pow(fabs(r)+sqrt(r2-q3), 1/3.);
double v = u == 0 ? 0 : q/u;
x[0] = (u+v)-a;
if (u == v || fabs(u-v) < 1e-12*fabs(u+v)) {
diff --git a/thirdparty/msdfgen/core/equation-solver.h b/thirdparty/msdfgen/core/equation-solver.h
index bae097b2b9..bfeeb15823 100644
--- a/thirdparty/msdfgen/core/equation-solver.h
+++ b/thirdparty/msdfgen/core/equation-solver.h
@@ -1,6 +1,8 @@
#pragma once
+#include "base.h"
+
namespace msdfgen {
// ax^2 + bx + c = 0
diff --git a/thirdparty/msdfgen/core/generator-config.h b/thirdparty/msdfgen/core/generator-config.h
index b430d37f3c..b7b153477f 100644
--- a/thirdparty/msdfgen/core/generator-config.h
+++ b/thirdparty/msdfgen/core/generator-config.h
@@ -1,7 +1,6 @@
#pragma once
-#include <cstdlib>
#include "BitmapRef.hpp"
#ifndef MSDFGEN_PUBLIC
diff --git a/thirdparty/msdfgen/core/msdf-error-correction.h b/thirdparty/msdfgen/core/msdf-error-correction.h
index d5384c9329..6318f184bb 100644
--- a/thirdparty/msdfgen/core/msdf-error-correction.h
+++ b/thirdparty/msdfgen/core/msdf-error-correction.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "Projection.h"
#include "Shape.h"
#include "BitmapRef.hpp"
diff --git a/thirdparty/msdfgen/core/pixel-conversion.hpp b/thirdparty/msdfgen/core/pixel-conversion.hpp
index 7e9b6d08f0..3ef8a2d1da 100644
--- a/thirdparty/msdfgen/core/pixel-conversion.hpp
+++ b/thirdparty/msdfgen/core/pixel-conversion.hpp
@@ -5,8 +5,6 @@
namespace msdfgen {
-typedef unsigned char byte;
-
inline byte pixelFloatToByte(float x) {
return byte(clamp(256.f*x, 255.f));
}
diff --git a/thirdparty/msdfgen/core/rasterization.h b/thirdparty/msdfgen/core/rasterization.h
index 82d0c73d95..8ba41cd645 100644
--- a/thirdparty/msdfgen/core/rasterization.h
+++ b/thirdparty/msdfgen/core/rasterization.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "Shape.h"
#include "Projection.h"
#include "Scanline.h"
diff --git a/thirdparty/msdfgen/core/render-sdf.h b/thirdparty/msdfgen/core/render-sdf.h
index 7f2d270b67..0fa695acf6 100644
--- a/thirdparty/msdfgen/core/render-sdf.h
+++ b/thirdparty/msdfgen/core/render-sdf.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "BitmapRef.hpp"
namespace msdfgen {
diff --git a/thirdparty/msdfgen/core/sdf-error-estimation.h b/thirdparty/msdfgen/core/sdf-error-estimation.h
index d2fd40d2b8..f9008222ab 100644
--- a/thirdparty/msdfgen/core/sdf-error-estimation.h
+++ b/thirdparty/msdfgen/core/sdf-error-estimation.h
@@ -1,7 +1,7 @@
#pragma once
-#include "Vector2.h"
+#include "Vector2.hpp"
#include "Shape.h"
#include "Projection.h"
#include "Scanline.h"
diff --git a/thirdparty/msdfgen/core/shape-description.cpp b/thirdparty/msdfgen/core/shape-description.cpp
index a096fa2541..32c515d120 100644
--- a/thirdparty/msdfgen/core/shape-description.cpp
+++ b/thirdparty/msdfgen/core/shape-description.cpp
@@ -244,34 +244,37 @@ bool writeShapeDescription(FILE *output, const Shape &shape) {
default:;
}
}
- if (const LinearSegment *e = dynamic_cast<const LinearSegment *>(&**edge)) {
- fprintf(output, "\t");
- writeCoord(output, e->p[0]);
- fprintf(output, ";\n");
- if (colorCode)
- fprintf(output, "\t\t%c;\n", colorCode);
- }
- if (const QuadraticSegment *e = dynamic_cast<const QuadraticSegment *>(&**edge)) {
- fprintf(output, "\t");
- writeCoord(output, e->p[0]);
- fprintf(output, ";\n\t\t");
- if (colorCode)
- fprintf(output, "%c", colorCode);
- fprintf(output, "(");
- writeCoord(output, e->p[1]);
- fprintf(output, ");\n");
- }
- if (const CubicSegment *e = dynamic_cast<const CubicSegment *>(&**edge)) {
- fprintf(output, "\t");
- writeCoord(output, e->p[0]);
- fprintf(output, ";\n\t\t");
- if (colorCode)
- fprintf(output, "%c", colorCode);
- fprintf(output, "(");
- writeCoord(output, e->p[1]);
- fprintf(output, "; ");
- writeCoord(output, e->p[2]);
- fprintf(output, ");\n");
+ const Point2 *p = (*edge)->controlPoints();
+ switch ((*edge)->type()) {
+ case (int) LinearSegment::EDGE_TYPE:
+ fprintf(output, "\t");
+ writeCoord(output, p[0]);
+ fprintf(output, ";\n");
+ if (colorCode)
+ fprintf(output, "\t\t%c;\n", colorCode);
+ break;
+ case (int) QuadraticSegment::EDGE_TYPE:
+ fprintf(output, "\t");
+ writeCoord(output, p[0]);
+ fprintf(output, ";\n\t\t");
+ if (colorCode)
+ fprintf(output, "%c", colorCode);
+ fprintf(output, "(");
+ writeCoord(output, p[1]);
+ fprintf(output, ");\n");
+ break;
+ case (int) CubicSegment::EDGE_TYPE:
+ fprintf(output, "\t");
+ writeCoord(output, p[0]);
+ fprintf(output, ";\n\t\t");
+ if (colorCode)
+ fprintf(output, "%c", colorCode);
+ fprintf(output, "(");
+ writeCoord(output, p[1]);
+ fprintf(output, "; ");
+ writeCoord(output, p[2]);
+ fprintf(output, ");\n");
+ break;
}
}
fprintf(output, "\t#\n");
diff --git a/thirdparty/msdfgen/core/shape-description.h b/thirdparty/msdfgen/core/shape-description.h
index 5df7c50a03..6e2e223674 100644
--- a/thirdparty/msdfgen/core/shape-description.h
+++ b/thirdparty/msdfgen/core/shape-description.h
@@ -1,7 +1,6 @@
#pragma once
-#include <cstdlib>
#include <cstdio>
#include "Shape.h"
diff --git a/thirdparty/msdfgen/msdfgen.h b/thirdparty/msdfgen/msdfgen.h
index c9ae4f52dc..dff8beb634 100644
--- a/thirdparty/msdfgen/msdfgen.h
+++ b/thirdparty/msdfgen/msdfgen.h
@@ -4,7 +4,7 @@
/*
* MULTI-CHANNEL SIGNED DISTANCE FIELD GENERATOR
* ---------------------------------------------
- * A utility by Viktor Chlumsky, (c) 2014 - 2022
+ * A utility by Viktor Chlumsky, (c) 2014 - 2023
*
* The technique used to generate multi-channel distance fields in this code
* has been developed by Viktor Chlumsky in 2014 for his master's thesis,
@@ -15,8 +15,9 @@
*
*/
+#include "core/base.h"
#include "core/arithmetics.hpp"
-#include "core/Vector2.h"
+#include "core/Vector2.hpp"
#include "core/Projection.h"
#include "core/Scanline.h"
#include "core/Shape.h"