summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/linux_builds.yml29
-rw-r--r--core/debugger/remote_debugger.cpp2
-rw-r--r--core/math/math_funcs.h13
-rw-r--r--core/variant/variant_utility.cpp5
-rw-r--r--doc/classes/@GlobalScope.xml20
-rw-r--r--doc/classes/Animation.xml13
-rw-r--r--doc/classes/AnimationNode.xml1
-rw-r--r--doc/classes/AnimationNodeAnimation.xml9
-rw-r--r--doc/classes/AudioStreamSample.xml2
-rw-r--r--doc/classes/FontData.xml39
-rw-r--r--doc/classes/Label.xml2
-rw-r--r--doc/classes/LineEdit.xml3
-rw-r--r--doc/classes/PhysicsDirectBodyState2D.xml5
-rw-r--r--doc/classes/PhysicsDirectBodyState3D.xml5
-rw-r--r--doc/classes/PhysicsServer2D.xml4
-rw-r--r--doc/classes/PhysicsServer3D.xml4
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RichTextLabel.xml6
-rw-r--r--doc/classes/TextParagraph.xml3
-rw-r--r--doc/classes/TextServer.xml69
-rw-r--r--doc/classes/TextServerExtension.xml60
-rw-r--r--doc/classes/VisualShaderNodeCustom.xml6
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp10
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp14
-rw-r--r--editor/animation_track_editor.cpp59
-rw-r--r--editor/animation_track_editor.h3
-rw-r--r--editor/editor_log.cpp4
-rw-r--r--editor/editor_node.cpp7
-rw-r--r--editor/editor_themes.cpp4
-rw-r--r--editor/editor_toaster.cpp6
-rw-r--r--editor/icons/PingPongLoop.svg1
-rw-r--r--editor/import/editor_import_plugin.cpp2
-rw-r--r--editor/import/resource_importer_bmfont.cpp29
-rw-r--r--editor/import/resource_importer_obj.cpp8
-rw-r--r--editor/import/resource_importer_scene.cpp22
-rw-r--r--editor/import/resource_importer_wav.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp9
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp4
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp10
-rw-r--r--editor/plugins/script_editor_plugin.cpp71
-rw-r--r--editor/plugins/script_editor_plugin.h1
-rw-r--r--editor/plugins/script_text_editor.cpp25
-rw-r--r--editor/plugins/text_control_editor_plugin.cpp375
-rw-r--r--editor/plugins/text_control_editor_plugin.h119
-rw-r--r--editor/plugins/text_editor.cpp25
-rw-r--r--editor/plugins/texture_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.cpp2
-rw-r--r--editor/rename_dialog.cpp6
-rw-r--r--main/main.cpp8
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/af.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ar.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/az.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/bg.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/bn.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/br.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ca.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/cs.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/da.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/de.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/el.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/eo.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/es.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/es_AR.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/et.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/eu.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/fa.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/fi.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/fil.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/fr.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ga.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/gl.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/he.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/hi.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/hr.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/hu.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/id.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/is.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/it.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ja.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ka.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/km.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ko.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/lt.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/lv.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/mi.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/mk.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ml.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/mr.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ms.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/nb.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/nl.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/or.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/pl.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/pt.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/pt_BR.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ro.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ru.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/si.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sk.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sl.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sq.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sr-Cyrl.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sr-Latn.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/sv.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ta.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/te.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/th.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/tr.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/tt.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/tzm.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/uk.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/ur_PK.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/vi.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/zh_CN.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/zh_HK.lproj/InfoPlist.strings0
-rw-r--r--misc/dist/osx_tools.app/Contents/Resources/zh_TW.lproj/InfoPlist.strings0
-rw-r--r--modules/bullet/bullet_physics_server.cpp9
-rw-r--r--modules/gdscript/gdscript_parser.cpp101
-rw-r--r--modules/gdscript/gdscript_vm.cpp2
-rw-r--r--modules/gltf/gltf_document.cpp2
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml5
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml29
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs18
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp117
-rw-r--r--modules/text_server_adv/text_server_adv.h16
-rw-r--r--modules/text_server_fb/text_server_fb.cpp131
-rw-r--r--modules/text_server_fb/text_server_fb.h16
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml49
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp17
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h1
-rw-r--r--modules/visual_script/visual_script_editor.cpp25
-rw-r--r--platform/android/export/export_plugin.cpp9
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java12
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java24
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotLib.java3
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java3
-rw-r--r--platform/android/java_godot_lib_jni.cpp3
-rw-r--r--platform/android/java_godot_lib_jni.h2
-rw-r--r--platform/android/os_android.cpp7
-rw-r--r--platform/android/os_android.h1
-rw-r--r--platform/linuxbsd/display_server_x11.cpp9
-rw-r--r--platform/windows/detect.py6
-rw-r--r--platform/windows/display_server_windows.cpp5
-rw-r--r--platform/windows/gl_manager_windows.cpp3
-rw-r--r--scene/2d/physics_body_2d.cpp4
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/animation/animation_blend_space_2d.cpp7
-rw-r--r--scene/animation/animation_blend_tree.cpp79
-rw-r--r--scene/animation/animation_blend_tree.h26
-rw-r--r--scene/animation/animation_player.cpp135
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--scene/animation/animation_tree.cpp273
-rw-r--r--scene/animation/animation_tree.h4
-rw-r--r--scene/gui/label.cpp10
-rw-r--r--scene/gui/line_edit.cpp18
-rw-r--r--scene/gui/line_edit.h4
-rw-r--r--scene/gui/rich_text_label.cpp29
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/resources/animation.cpp679
-rw-r--r--scene/resources/animation.h24
-rw-r--r--scene/resources/audio_stream_sample.cpp6
-rw-r--r--scene/resources/audio_stream_sample.h2
-rw-r--r--scene/resources/default_theme/default_theme.cpp2
-rw-r--r--scene/resources/font.cpp60
-rw-r--r--scene/resources/font.h9
-rw-r--r--scene/resources/text_paragraph.cpp14
-rw-r--r--scene/resources/text_paragraph.h3
-rw-r--r--scene/resources/visual_shader.cpp4
-rw-r--r--scene/resources/visual_shader.h4
-rw-r--r--servers/physics_2d/godot_body_2d.cpp2
-rw-r--r--servers/physics_2d/godot_body_2d.h1
-rw-r--r--servers/physics_2d/godot_body_direct_state_2d.cpp4
-rw-r--r--servers/physics_2d/godot_body_direct_state_2d.h1
-rw-r--r--servers/physics_2d/godot_physics_server_2d.cpp9
-rw-r--r--servers/physics_3d/godot_body_3d.cpp2
-rw-r--r--servers/physics_3d/godot_body_3d.h1
-rw-r--r--servers/physics_3d/godot_body_direct_state_3d.cpp4
-rw-r--r--servers/physics_3d/godot_body_direct_state_3d.h1
-rw-r--r--servers/physics_3d/godot_physics_server_3d.cpp9
-rw-r--r--servers/physics_server_2d.cpp2
-rw-r--r--servers/physics_server_2d.h1
-rw-r--r--servers/physics_server_3d.cpp2
-rw-r--r--servers/physics_server_3d.h1
-rw-r--r--servers/rendering/shader_language.cpp18
-rw-r--r--servers/text/text_server_extension.cpp60
-rw-r--r--servers/text/text_server_extension.h20
-rw-r--r--servers/text_server.cpp19
-rw-r--r--servers/text_server.h20
-rw-r--r--tests/SCsub5
-rw-r--r--tests/core/io/test_config_file.h (renamed from tests/test_config_file.h)1
-rw-r--r--tests/core/io/test_file_access.h (renamed from tests/test_file_access.h)3
-rw-r--r--tests/core/io/test_image.h (renamed from tests/test_image.h)4
-rw-r--r--tests/core/io/test_json.h (renamed from tests/test_json.h)0
-rw-r--r--tests/core/io/test_marshalls.h (renamed from tests/test_marshalls.h)0
-rw-r--r--tests/core/io/test_pck_packer.h (renamed from tests/test_pck_packer.h)1
-rw-r--r--tests/core/io/test_resource.h (renamed from tests/test_resource.h)0
-rw-r--r--tests/core/io/test_xml_parser.h (renamed from tests/test_xml_parser.h)3
-rw-r--r--tests/core/math/test_aabb.h (renamed from tests/test_aabb.h)4
-rw-r--r--tests/core/math/test_astar.h (renamed from tests/test_astar.h)5
-rw-r--r--tests/core/math/test_basis.h (renamed from tests/test_basis.h)3
-rw-r--r--tests/core/math/test_color.h (renamed from tests/test_color.h)2
-rw-r--r--tests/core/math/test_expression.h (renamed from tests/test_expression.h)0
-rw-r--r--tests/core/math/test_geometry_2d.h (renamed from tests/test_geometry_2d.h)1
-rw-r--r--tests/core/math/test_geometry_3d.h (renamed from tests/test_geometry_3d.h)4
-rw-r--r--tests/core/math/test_math.cpp (renamed from tests/test_math.cpp)14
-rw-r--r--tests/core/math/test_math.h (renamed from tests/test_math.h)2
-rw-r--r--tests/core/math/test_random_number_generator.h (renamed from tests/test_random_number_generator.h)0
-rw-r--r--tests/core/math/test_rect2.h (renamed from tests/test_rect2.h)0
-rw-r--r--tests/core/object/test_class_db.h (renamed from tests/test_class_db.h)9
-rw-r--r--tests/core/object/test_method_bind.h (renamed from tests/test_method_bind.h)4
-rw-r--r--tests/core/object/test_object.h (renamed from tests/test_object.h)4
-rw-r--r--tests/core/string/test_node_path.h (renamed from tests/test_node_path.h)2
-rw-r--r--tests/core/string/test_string.h (renamed from tests/test_string.h)8
-rw-r--r--tests/core/string/test_translation.h (renamed from tests/test_translation.h)3
-rw-r--r--tests/core/templates/test_command_queue.h (renamed from tests/test_command_queue.h)4
-rw-r--r--tests/core/templates/test_list.h (renamed from tests/test_list.h)0
-rw-r--r--tests/core/templates/test_local_vector.h (renamed from tests/test_local_vector.h)0
-rw-r--r--tests/core/templates/test_lru.h (renamed from tests/test_lru.h)1
-rw-r--r--tests/core/templates/test_oa_hash_map.cpp (renamed from tests/test_oa_hash_map.cpp)0
-rw-r--r--tests/core/templates/test_oa_hash_map.h (renamed from tests/test_oa_hash_map.h)2
-rw-r--r--tests/core/templates/test_ordered_hash_map.h (renamed from tests/test_ordered_hash_map.h)3
-rw-r--r--tests/core/templates/test_paged_array.h (renamed from tests/test_paged_array.h)0
-rw-r--r--tests/core/templates/test_vector.h (renamed from tests/test_vector.h)0
-rw-r--r--tests/core/test_crypto.h (renamed from tests/test_crypto.h)1
-rw-r--r--tests/core/test_hashing_context.h (renamed from tests/test_hashing_context.h)0
-rw-r--r--tests/core/test_time.h (renamed from tests/test_time.h)0
-rw-r--r--tests/core/variant/test_array.h (renamed from tests/test_array.h)6
-rw-r--r--tests/core/variant/test_dictionary.h (renamed from tests/test_dictionary.h)3
-rw-r--r--tests/core/variant/test_variant.h (renamed from tests/test_variant.h)0
-rw-r--r--tests/scene/test_code_edit.h (renamed from tests/test_code_edit.h)5
-rw-r--r--tests/scene/test_curve.h (renamed from tests/test_curve.h)2
-rw-r--r--tests/scene/test_gradient.h (renamed from tests/test_gradient.h)2
-rw-r--r--tests/scene/test_gui.cpp (renamed from tests/test_gui.cpp)13
-rw-r--r--tests/scene/test_gui.h (renamed from tests/test_gui.h)2
-rw-r--r--tests/scene/test_path_3d.h (renamed from tests/test_path_3d.h)1
-rw-r--r--tests/scene/test_path_follow_2d.h (renamed from tests/test_path_follow_2d.h)1
-rw-r--r--tests/scene/test_path_follow_3d.h (renamed from tests/test_path_follow_3d.h)1
-rw-r--r--tests/servers/test_physics_2d.cpp (renamed from tests/test_physics_2d.cpp)5
-rw-r--r--tests/servers/test_physics_2d.h (renamed from tests/test_physics_2d.h)2
-rw-r--r--tests/servers/test_physics_3d.cpp (renamed from tests/test_physics_3d.cpp)6
-rw-r--r--tests/servers/test_physics_3d.h (renamed from tests/test_physics_3d.h)2
-rw-r--r--tests/servers/test_render.cpp (renamed from tests/test_render.cpp)5
-rw-r--r--tests/servers/test_render.h (renamed from tests/test_render.h)4
-rw-r--r--tests/servers/test_shader_lang.cpp (renamed from tests/test_shader_lang.cpp)5
-rw-r--r--tests/servers/test_shader_lang.h (renamed from tests/test_shader_lang.h)2
-rw-r--r--tests/servers/test_text_server.h (renamed from tests/test_text_server.h)0
-rw-r--r--tests/test_macros.h6
-rw-r--r--tests/test_main.cpp102
-rw-r--r--tests/test_tools.h2
-rw-r--r--tests/test_utils.cpp2
-rw-r--r--tests/test_utils.h2
256 files changed, 2762 insertions, 862 deletions
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index f6d11e2efe..15bd4bc69b 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -18,13 +18,15 @@ jobs:
fail-fast: false
matrix:
include:
- - name: Editor (target=release_debug, tools=yes, tests=yes)
- cache-name: linux-editor
+ - name: Editor w/ Mono (target=release_debug, tools=yes, tests=yes)
+ cache-name: linux-editor-mono
target: release_debug
tools: true
- tests: true
+ tests: false # Disabled due freeze caused by mix Mono build and CI
+ sconsflags: module_mono_enabled=yes mono_glue=no
doc-test: true
- bin: "./bin/godot.linuxbsd.opt.tools.64"
+ bin: "./bin/godot.linuxbsd.opt.tools.64.mono"
+ build-mono: true
artifact: true
- name: Editor and sanitizers (target=debug, tools=yes, tests=yes, use_asan=yes, use_ubsan=yes)
@@ -36,6 +38,7 @@ jobs:
proj-test: true
godot-cpp-test: true
bin: "./bin/godot.linuxbsd.tools.64s"
+ build-mono: false
# Skip 2GiB artifact speeding up action.
artifact: false
@@ -45,6 +48,7 @@ jobs:
tools: false
tests: false
sconsflags: module_mono_enabled=yes mono_glue=no debug_symbols=no
+ build-mono: false
artifact: true
steps:
@@ -80,11 +84,26 @@ jobs:
tools: ${{ matrix.tools }}
tests: ${{ matrix.tests }}
+ - name: Generate Mono glue
+ if: ${{ matrix.build-mono }}
+ run: |
+ ${{ matrix.bin }} --headless --generate-mono-glue modules/mono/glue || true
+
+ # Rebuild with mono
+ - name: Compilation (mono_glue=yes)
+ uses: ./.github/actions/godot-build
+ if: ${{ matrix.build-mono }}
+ with:
+ sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} mono_glue=yes
+ platform: linuxbsd
+ target: ${{ matrix.target }}
+ tools: ${{ matrix.tools }}
+
# Execute unit tests for the editor
- name: Unit tests
if: ${{ matrix.tests }}
run: |
- ${{ matrix.bin }} --test
+ ${{ matrix.bin }} --test --headless
# Check class reference
- name: Check for class reference updates
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index 4607bd2f3f..87e65e592a 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -475,7 +475,7 @@ void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *
}
// send_error will lock internally.
- rd->script_debugger->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type, si);
+ rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si);
}
void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error) {
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index baff10af98..b3eabd3e7a 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -291,6 +291,19 @@ public:
return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
}
+ static _ALWAYS_INLINE_ float fract(float value) {
+ return value - floor(value);
+ }
+ static _ALWAYS_INLINE_ double fract(double value) {
+ return value - floor(value);
+ }
+ static _ALWAYS_INLINE_ float pingpong(float value, float length) {
+ return (length != 0.0f) ? abs(fract((value - length) / (length * 2.0f)) * length * 2.0f - length) : 0.0f;
+ }
+ static _ALWAYS_INLINE_ double pingpong(double value, double length) {
+ return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0;
+ }
+
// double only, as these functions are mainly used by the editor and not performance-critical,
static double ease(double p_x, double p_c);
static int step_decimals(double p_step);
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 666b582e39..e89bdd4faa 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -275,6 +275,10 @@ struct VariantUtilityFunctions {
return Math::wrapf(value, min, max);
}
+ static inline double pingpong(double value, double length) {
+ return Math::pingpong(value, length);
+ }
+
static inline Variant max(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (p_argcount < 2) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
@@ -1226,6 +1230,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(clampf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(nearest_po2, sarray("value"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(pingpong, sarray("value", "length"), Variant::UTILITY_FUNC_TYPE_MATH);
// Random
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index ea49b6b634..345a769ca5 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -566,6 +566,26 @@
[b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
</description>
</method>
+ <method name="pingpong">
+ <return type="float" />
+ <argument index="0" name="value" type="float" />
+ <argument index="1" name="length" type="float" />
+ <description>
+ Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
+ [codeblock]
+ pingpong(-3.0, 3.0) # Returns 3
+ pingpong(-2.0, 3.0) # Returns 2
+ pingpong(-1.0, 3.0) # Returns 1
+ pingpong(0.0, 3.0) # Returns 0
+ pingpong(1.0, 3.0) # Returns 1
+ pingpong(2.0, 3.0) # Returns 2
+ pingpong(3.0, 3.0) # Returns 3
+ pingpong(4.0, 3.0) # Returns 2
+ pingpong(5.0, 3.0) # Returns 1
+ pingpong(6.0, 3.0) # Returns 0
+ [/codeblock]
+ </description>
+ </method>
<method name="posmod">
<return type="int" />
<argument index="0" name="x" type="int" />
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index e3bb60f6de..dca943dec6 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -551,8 +551,8 @@
The total length of the animation (in seconds).
[b]Note:[/b] Length is not delimited by the last key, as this one may be before or after the end to ensure correct interpolation and looping.
</member>
- <member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
- A flag indicating that the animation must loop. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
+ <member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode" default="0">
+ Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
</member>
<member name="step" type="float" setter="set_step" getter="get_step" default="0.1">
The animation step value.
@@ -610,5 +610,14 @@
<constant name="UPDATE_CAPTURE" value="3" enum="UpdateMode">
Same as linear interpolation, but also interpolates from the current value (i.e. dynamically at runtime) if the first key isn't at 0 seconds.
</constant>
+ <constant name="LOOP_NONE" value="0" enum="LoopMode">
+ At both ends of the animation, the animation will stop playing.
+ </constant>
+ <constant name="LOOP_LINEAR" value="1" enum="LoopMode">
+ At both ends of the animation, the animation will be repeated without changing the playback direction.
+ </constant>
+ <constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
+ Repeats playback and reverse playback at both ends of the animation.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 173ff43d2a..6bc44ea0a0 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -73,6 +73,7 @@
<argument index="2" name="delta" type="float" />
<argument index="3" name="seeked" type="bool" />
<argument index="4" name="blend" type="float" />
+ <argument index="5" name="pingponged" type="int" default="0" />
<description>
Blend an animation by [code]blend[/code] amount (name must be valid in the linked [AnimationPlayer]). A [code]time[/code] and [code]delta[/code] may be passed, as well as whether [code]seek[/code] happened.
</description>
diff --git a/doc/classes/AnimationNodeAnimation.xml b/doc/classes/AnimationNodeAnimation.xml
index 668a35226f..076e675007 100644
--- a/doc/classes/AnimationNodeAnimation.xml
+++ b/doc/classes/AnimationNodeAnimation.xml
@@ -15,5 +15,14 @@
<member name="animation" type="StringName" setter="set_animation" getter="get_animation" default="&amp;&quot;&quot;">
Animation to use as an output. It is one of the animations provided by [member AnimationTree.anim_player].
</member>
+ <member name="play_mode" type="int" setter="set_play_mode" getter="get_play_mode" enum="AnimationNodeAnimation.PlayMode" default="0">
+ Determines the playback direction of the animation.
+ </member>
</members>
+ <constants>
+ <constant name="PLAY_MODE_FORWARD" value="0" enum="PlayMode">
+ </constant>
+ <constant name="PLAY_MODE_BACKWARD" value="1" enum="PlayMode">
+ </constant>
+ </constants>
</class>
diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml
index 7e1155d89b..df7b5ff1c7 100644
--- a/doc/classes/AudioStreamSample.xml
+++ b/doc/classes/AudioStreamSample.xml
@@ -61,7 +61,7 @@
<constant name="LOOP_FORWARD" value="1" enum="LoopMode">
Audio loops the data between [member loop_begin] and [member loop_end], playing forward only.
</constant>
- <constant name="LOOP_PING_PONG" value="2" enum="LoopMode">
+ <constant name="LOOP_PINGPONG" value="2" enum="LoopMode">
Audio loops the data between [member loop_begin] and [member loop_end], playing back and forth.
</constant>
<constant name="LOOP_BACKWARD" value="3" enum="LoopMode">
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 04932ddd2d..ccfe861c92 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -93,6 +93,24 @@
Returns font descent (number of pixels below the baseline).
</description>
</method>
+ <method name="get_font_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns font family name.
+ </description>
+ </method>
+ <method name="get_font_style" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns font style flags, see [enum TextServer.FontStyle].
+ </description>
+ </method>
+ <method name="get_font_style_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns font style name.
+ </description>
+ </method>
<method name="get_glyph_advance" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="cache_index" type="int" />
@@ -463,6 +481,27 @@
Sets the font descent (number of pixels below the baseline).
</description>
</method>
+ <method name="set_font_name">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <description>
+ Sets the font family name.
+ </description>
+ </method>
+ <method name="set_font_style">
+ <return type="void" />
+ <argument index="0" name="style" type="int" />
+ <description>
+ Sets the font style flags, see [enum TextServer.FontStyle].
+ </description>
+ </method>
+ <method name="set_font_style_name">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <description>
+ Sets the font style name.
+ </description>
+ </method>
<method name="set_force_autohinter">
<return type="void" />
<argument index="0" name="force_autohinter" type="bool" />
diff --git a/doc/classes/Label.xml b/doc/classes/Label.xml
index 3c349e052f..24ebf08c36 100644
--- a/doc/classes/Label.xml
+++ b/doc/classes/Label.xml
@@ -194,7 +194,7 @@
The vertical offset of the text's shadow.
</theme_item>
<theme_item name="shadow_outline_size" data_type="constant" type="int" default="1">
- Shadow outline size. If set to 1 or greater, the shadow will be displayed around the whole text as an outline.
+ The size of the shadow outline.
</theme_item>
</theme_items>
</class>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index f79ba5a16f..a75bd2f704 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -196,6 +196,9 @@
<member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length_enabled" getter="is_expand_to_text_length_enabled" default="false">
If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened.
</member>
+ <member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
+ If [code]true[/code], the [LineEdit] don't display decoration.
+ </member>
<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" override="true" enum="Control.FocusMode" default="2" />
<member name="language" type="String" setter="set_language" getter="get_language" default="&quot;&quot;">
Language code used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
diff --git a/doc/classes/PhysicsDirectBodyState2D.xml b/doc/classes/PhysicsDirectBodyState2D.xml
index aa189f17f4..3fe9cd776c 100644
--- a/doc/classes/PhysicsDirectBodyState2D.xml
+++ b/doc/classes/PhysicsDirectBodyState2D.xml
@@ -150,7 +150,10 @@
The body's rotational velocity.
</member>
<member name="center_of_mass" type="Vector2" setter="" getter="get_center_of_mass">
- The body's center of mass.
+ The body's center of mass position relative to the body's center in the global coordinate system.
+ </member>
+ <member name="center_of_mass_local" type="Vector2" setter="" getter="get_center_of_mass_local">
+ The body's center of mass position in the body's local coordinate system.
</member>
<member name="inverse_inertia" type="float" setter="" getter="get_inverse_inertia">
The inverse of the inertia of the body.
diff --git a/doc/classes/PhysicsDirectBodyState3D.xml b/doc/classes/PhysicsDirectBodyState3D.xml
index 9bc5dbd6b9..efa9eb9cd6 100644
--- a/doc/classes/PhysicsDirectBodyState3D.xml
+++ b/doc/classes/PhysicsDirectBodyState3D.xml
@@ -159,7 +159,10 @@
The body's rotational velocity.
</member>
<member name="center_of_mass" type="Vector3" setter="" getter="get_center_of_mass">
- The body's center of mass.
+ The body's center of mass position relative to the body's center in the global coordinate system.
+ </member>
+ <member name="center_of_mass_local" type="Vector3" setter="" getter="get_center_of_mass_local">
+ The body's center of mass position in the body's local coordinate system.
</member>
<member name="inverse_inertia" type="Vector3" setter="" getter="get_inverse_inertia">
The inverse of the inertia of the body.
diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml
index e44bf71e8d..6f88707259 100644
--- a/doc/classes/PhysicsServer2D.xml
+++ b/doc/classes/PhysicsServer2D.xml
@@ -346,7 +346,7 @@
<return type="PhysicsDirectBodyState2D" />
<argument index="0" name="body" type="RID" />
<description>
- Returns the [PhysicsDirectBodyState2D] of the body.
+ Returns the [PhysicsDirectBodyState2D] of the body. Returns [code]null[/code] if the body is destroyed or removed from the physics space.
</description>
</method>
<method name="body_get_max_contacts_reported" qualifiers="const">
@@ -919,7 +919,7 @@
Constant to set/get a body's inertia.
</constant>
<constant name="BODY_PARAM_CENTER_OF_MASS" value="4" enum="BodyParameter">
- Constant to set/get a body's center of mass.
+ Constant to set/get a body's center of mass position in the body's local coordinate system.
</constant>
<constant name="BODY_PARAM_GRAVITY_SCALE" value="5" enum="BodyParameter">
Constant to set/get a body's gravity multiplier.
diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml
index 0c34cf8092..b144c2c299 100644
--- a/doc/classes/PhysicsServer3D.xml
+++ b/doc/classes/PhysicsServer3D.xml
@@ -320,7 +320,7 @@
<return type="PhysicsDirectBodyState3D" />
<argument index="0" name="body" type="RID" />
<description>
- Returns the [PhysicsDirectBodyState3D] of the body.
+ Returns the [PhysicsDirectBodyState3D] of the body. Returns [code]null[/code] if the body is destroyed or removed from the physics space.
</description>
</method>
<method name="body_get_max_contacts_reported" qualifiers="const">
@@ -1287,7 +1287,7 @@
Constant to set/get a body's inertia.
</constant>
<constant name="BODY_PARAM_CENTER_OF_MASS" value="4" enum="BodyParameter">
- Constant to set/get a body's center of mass.
+ Constant to set/get a body's center of mass position in the body's local coordinate system.
</constant>
<constant name="BODY_PARAM_GRAVITY_SCALE" value="5" enum="BodyParameter">
Constant to set/get a body's gravity multiplier.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 30409814d2..80ef73d824 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1315,9 +1315,9 @@
</member>
<member name="mono/profiler/enabled" type="bool" setter="" getter="" default="false">
</member>
- <member name="mono/project/auto_update_project" type="bool" setter="" getter="" default="true">
- </member>
- <member name="mono/unhandled_exception_policy" type="int" setter="" getter="" default="0">
+ <member name="mono/runtime/unhandled_exception_policy" type="int" setter="" getter="" default="0">
+ The policy to use for unhandled Mono (C#) exceptions. The default "Terminate Application" exits the project as soon as an unhandled exception is thrown. "Log Error" logs an error message to the console instead, and will not interrupt the project execution when an unhandled exception is thrown.
+ [b]Note:[/b] The unhandled exception policy is always set to "Log Error" in the editor, which also includes C# [code]tool[/code] scripts running within the editor as well as editor plugin code.
</member>
<member name="navigation/2d/default_cell_size" type="int" setter="" getter="" default="10">
Default cell size for 2D navigation maps. See [method NavigationServer2D.map_set_cell_size].
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 0fd25615ba..3255c748b7 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -586,15 +586,15 @@
<theme_item name="selection_color" data_type="color" type="Color" default="Color(0.1, 0.1, 1, 0.8)">
The color of the selection box.
</theme_item>
- <theme_item name="shadow_as_outline" data_type="constant" type="int" default="0">
- Boolean value. If 1 ([code]true[/code]), the shadow will be displayed around the whole text as an outline.
- </theme_item>
<theme_item name="shadow_offset_x" data_type="constant" type="int" default="1">
The horizontal offset of the font's shadow.
</theme_item>
<theme_item name="shadow_offset_y" data_type="constant" type="int" default="1">
The vertical offset of the font's shadow.
</theme_item>
+ <theme_item name="shadow_outline_size" data_type="constant" type="int" default="1">
+ The size of the shadow outline.
+ </theme_item>
<theme_item name="table_border" data_type="color" type="Color" default="Color(0, 0, 0, 0)">
The default cell border color.
</theme_item>
diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml
index 5bd6b0572c..a7122b827e 100644
--- a/doc/classes/TextParagraph.xml
+++ b/doc/classes/TextParagraph.xml
@@ -278,6 +278,9 @@
<member name="align" type="int" setter="set_align" getter="get_align" enum="HAlign" default="0">
Paragraph horizontal alignment.
</member>
+ <member name="custom_punctuation" type="String" setter="set_custom_punctuation" getter="get_custom_punctuation" default="&quot;&quot;">
+ Custom punctuation character list, used for word breaking. If set to empty string, server defaults are used.
+ </member>
<member name="direction" type="int" setter="set_direction" getter="get_direction" enum="TextServer.Direction" default="0">
Text writing direction.
</member>
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 3e32afe370..e1c05165de 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -254,6 +254,13 @@
Returns source font size used to generate MSDF textures.
</description>
</method>
+ <method name="font_get_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font family name.
+ </description>
+ </method>
<method name="font_get_oversampling" qualifiers="const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@@ -300,6 +307,20 @@
Returns extra spacing added between glyphs in pixels.
</description>
</method>
+ <method name="font_get_style" qualifiers="const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font style flags, see [enum FontStyle].
+ </description>
+ </method>
+ <method name="font_get_style_name" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font style name.
+ </description>
+ </method>
<method name="font_get_supported_chars" qualifiers="const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
@@ -634,6 +655,14 @@
[b]Note:[/b] MSDF font rendering does not render glyphs with overlapping shapes correctly. Overlapping shapes are not valid per the OpenType standard, but are still commonly found in many font files, especially those converted by Google Fonts. To avoid issues with overlapping glyphs, consider downloading the font file directly from the type foundry instead of relying on Google Fonts.
</description>
</method>
+ <method name="font_set_name">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="name" type="String" />
+ <description>
+ Sets the font family name.
+ </description>
+ </method>
<method name="font_set_oversampling">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@@ -670,6 +699,22 @@
Sets extra spacing added between glyphs in pixels.
</description>
</method>
+ <method name="font_set_style">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="style" type="int" />
+ <description>
+ Sets the font style flags, see [enum FontStyle].
+ </description>
+ </method>
+ <method name="font_set_style_name">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="name" type="String" />
+ <description>
+ Set the font style name.
+ </description>
+ </method>
<method name="font_set_texture_image">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@@ -917,6 +962,13 @@
Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
</description>
</method>
+ <method name="shaped_text_get_custom_punctuation" qualifiers="const">
+ <return type="String" />
+ <argument index="0" name="shaped" type="RID" />
+ <description>
+ 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="const">
<return type="float" />
<argument index="0" name="shaped" type="RID" />
@@ -1167,6 +1219,14 @@
Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
+ <method name="shaped_text_set_custom_punctuation">
+ <return type="void" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="punct" type="String" />
+ <description>
+ 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">
<return type="void" />
<argument index="0" name="shaped" type="RID" />
@@ -1402,5 +1462,14 @@
<constant name="SPACING_BOTTOM" value="3" enum="SpacingType">
Spacing at the bottom of the line.
</constant>
+ <constant name="FONT_BOLD" value="1" enum="FontStyle">
+ Font is bold.
+ </constant>
+ <constant name="FONT_ITALIC" value="2" enum="FontStyle">
+ Font is italic or oblique.
+ </constant>
+ <constant name="FONT_FIXED_WIDTH" value="4" enum="FontStyle">
+ Font have fixed-width characters.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index 99382d5463..684a1aa755 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -254,6 +254,13 @@
Returns source font size used to generate MSDF textures.
</description>
</method>
+ <method name="_font_get_name" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font family name.
+ </description>
+ </method>
<method name="_font_get_oversampling" qualifiers="virtual const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@@ -300,6 +307,20 @@
Returns extra spacing added between glyphs in pixels.
</description>
</method>
+ <method name="_font_get_style" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font style flags, see [enum TextServer.FontStyle].
+ </description>
+ </method>
+ <method name="_font_get_style_name" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="font_rid" type="RID" />
+ <description>
+ Returns font style name.
+ </description>
+ </method>
<method name="_font_get_supported_chars" qualifiers="virtual const">
<return type="String" />
<argument index="0" name="font_rid" type="RID" />
@@ -641,6 +662,14 @@
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.
</description>
</method>
+ <method name="_font_set_name" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="name" type="String" />
+ <description>
+ Sets the font family name.
+ </description>
+ </method>
<method name="_font_set_oversampling" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@@ -677,6 +706,22 @@
Sets extra spacing added between glyphs in pixels.
</description>
</method>
+ <method name="_font_set_style" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="style" type="int" />
+ <description>
+ Sets the font style flags, see [enum TextServer.FontStyle].
+ </description>
+ </method>
+ <method name="_font_set_style_name" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="font_rid" type="RID" />
+ <argument index="1" name="name_style" type="String" />
+ <description>
+ Sets the font style name.
+ </description>
+ </method>
<method name="_font_set_texture_image" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
@@ -924,6 +969,13 @@
Returns shapes of the carets corresponding to the character offset [code]position[/code] in the text. Returned caret shape is 1 pixel wide rectangle.
</description>
</method>
+ <method name="_shaped_text_get_custom_punctuation" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="shaped" type="RID" />
+ <description>
+ 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" />
<argument index="0" name="shaped" type="RID" />
@@ -1176,6 +1228,14 @@
Override ranges should cover full source text without overlaps. BiDi algorithm will be used on each range separately.
</description>
</method>
+ <method name="_shaped_text_set_custom_punctuation" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="shaped" type="RID" />
+ <argument index="1" name="punct" type="String" />
+ <description>
+ 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">
<return type="void" />
<argument index="0" name="shaped" type="RID" />
diff --git a/doc/classes/VisualShaderNodeCustom.xml b/doc/classes/VisualShaderNodeCustom.xml
index b87b59c3e4..58f345b8f9 100644
--- a/doc/classes/VisualShaderNodeCustom.xml
+++ b/doc/classes/VisualShaderNodeCustom.xml
@@ -27,8 +27,8 @@
<return type="String" />
<argument index="0" name="input_vars" type="PackedStringArray" />
<argument index="1" name="output_vars" type="String[]" />
- <argument index="2" name="mode" type="int" />
- <argument index="3" name="type" type="int" />
+ <argument index="2" name="mode" type="int" enum="Shader.Mode" />
+ <argument index="3" name="type" type="int" enum="VisualShader.Type" />
<description>
Override this method to define the actual shader code of the associated custom node. The shader code should be returned as a string, which can have multiple lines (the [code]"""[/code] multiline string construct can be used for convenience).
The [code]input_vars[/code] and [code]output_vars[/code] arrays contain the string names of the various input and output variables, as defined by [code]_get_input_*[/code] and [code]_get_output_*[/code] virtual methods in this class.
@@ -46,7 +46,7 @@
</method>
<method name="_get_global_code" qualifiers="virtual const">
<return type="String" />
- <argument index="0" name="mode" type="int" />
+ <argument index="0" name="mode" type="int" enum="Shader.Mode" />
<description>
Override this method to add shader code on top of the global shader, to define your own standard library of reusable methods, varyings, constants, uniforms, etc. The shader code should be returned as a string, which can have multiple lines (the [code]"""[/code] multiline string construct can be used for convenience).
Be careful with this functionality as it can cause name conflicts with other custom nodes, so be sure to give the defined entities unique names.
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 1233f0f632..06058f2d39 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -192,7 +192,7 @@ Error AudioDriverPulseAudio::init_device() {
Error err = detect_channels();
if (err != OK) {
// This most likely means there are no sinks.
- ERR_PRINT("PulseAudio: init device failed to detect number of channels");
+ ERR_PRINT("PulseAudio: init device failed to detect number of output channels");
return err;
}
@@ -212,7 +212,7 @@ Error AudioDriverPulseAudio::init_device() {
break;
default:
- WARN_PRINT("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
+ WARN_PRINT("PulseAudio: Unsupported number of output channels: " + itos(pa_map.channels));
pa_channel_map_init_stereo(&pa_map);
channels = 2;
break;
@@ -222,8 +222,8 @@ Error AudioDriverPulseAudio::init_device() {
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
pa_buffer_size = buffer_frames * pa_map.channels;
- print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " channels");
- print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
+ print_verbose("PulseAudio: detected " + itos(pa_map.channels) + " output channels");
+ print_verbose("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated output latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
@@ -700,6 +700,8 @@ Error AudioDriverPulseAudio::capture_init_device() {
break;
}
+ print_verbose("PulseAudio: detected " + itos(pa_rec_map.channels) + " input channels");
+
pa_sample_spec spec;
spec.format = PA_SAMPLE_S16LE;
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 3b2f078120..520d4808fb 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -121,6 +121,12 @@ const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
static bool default_render_device_changed = false;
static bool default_capture_device_changed = false;
+// Silence warning due to a COM API weirdness (GH-35194).
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
class CMMNotificationClient : public IMMNotificationClient {
LONG _cRef = 1;
IMMDeviceEnumerator *_pEnumerator = nullptr;
@@ -162,7 +168,7 @@ public:
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
return S_OK;
- };
+ }
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
return S_OK;
@@ -189,6 +195,10 @@ public:
}
};
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
static CMMNotificationClient notif_client;
Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) {
@@ -373,7 +383,7 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c
hr = p_device->audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, p_capture ? REFTIMES_PER_SEC : 0, 0, pwfex, nullptr);
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_CANT_OPEN, "WASAPI: Initialize failed with error 0x" + String::num_uint64(hr, 16) + ".");
UINT32 max_frames;
- HRESULT hr = p_device->audio_client->GetBufferSize(&max_frames);
+ hr = p_device->audio_client->GetBufferSize(&max_frames);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
// Due to WASAPI Shared Mode we have no control of the buffer size
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index a85a4450a6..b15636f775 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -1377,8 +1377,20 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
void AnimationTimelineEdit::_anim_loop_pressed() {
undo_redo->create_action(TTR("Change Animation Loop"));
- undo_redo->add_do_method(animation.ptr(), "set_loop", loop->is_pressed());
- undo_redo->add_undo_method(animation.ptr(), "set_loop", animation->has_loop());
+ switch (animation->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_LINEAR);
+ } break;
+ case Animation::LoopMode::LOOP_LINEAR: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_PINGPONG);
+ } break;
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LoopMode::LOOP_NONE);
+ } break;
+ default:
+ break;
+ }
+ undo_redo->add_undo_method(animation.ptr(), "set_loop_mode", animation->get_loop_mode());
undo_redo->commit_action();
}
@@ -1664,7 +1676,24 @@ void AnimationTimelineEdit::update_values() {
length->set_tooltip(TTR("Animation length (seconds)"));
time_icon->set_tooltip(TTR("Animation length (seconds)"));
}
- loop->set_pressed(animation->has_loop());
+
+ switch (animation->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
+ loop->set_pressed(false);
+ } break;
+ case Animation::LoopMode::LOOP_LINEAR: {
+ loop->set_icon(get_theme_icon("Loop", "EditorIcons"));
+ loop->set_pressed(true);
+ } break;
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ loop->set_icon(get_theme_icon("PingPongLoop", "EditorIcons"));
+ loop->set_pressed(true);
+ } break;
+ default:
+ break;
+ }
+
editing = false;
}
@@ -2110,25 +2139,25 @@ void AnimationTrackEdit::_notification(int p_what) {
Ref<Texture2D> icon = wrap_icon[loop_wrap ? 1 : 0];
- loop_mode_rect.position.x = ofs;
- loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
- loop_mode_rect.size = icon->get_size();
+ loop_wrap_rect.position.x = ofs;
+ loop_wrap_rect.position.y = int(get_size().height - icon->get_height()) / 2;
+ loop_wrap_rect.size = icon->get_size();
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
- draw_texture(icon, loop_mode_rect.position);
+ draw_texture(icon, loop_wrap_rect.position);
}
- loop_mode_rect.position.y = 0;
- loop_mode_rect.size.y = get_size().height;
+ loop_wrap_rect.position.y = 0;
+ loop_wrap_rect.size.y = get_size().height;
ofs += icon->get_width() + hsep;
- loop_mode_rect.size.x += hsep;
+ loop_wrap_rect.size.x += hsep;
if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
- loop_mode_rect.size.x += down_icon->get_width();
+ loop_wrap_rect.size.x += down_icon->get_width();
} else {
- loop_mode_rect = Rect2();
+ loop_wrap_rect = Rect2();
}
ofs += down_icon->get_width();
@@ -2478,7 +2507,7 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const {
return TTR("Interpolation Mode");
}
- if (loop_mode_rect.has_point(p_pos)) {
+ if (loop_wrap_rect.has_point(p_pos)) {
return TTR("Loop Wrap Mode (Interpolate end with beginning on loop)");
}
@@ -2681,7 +2710,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
accept_event();
}
- if (loop_mode_rect.has_point(pos)) {
+ if (loop_wrap_rect.has_point(pos)) {
if (!menu) {
menu = memnew(PopupMenu);
add_child(menu);
@@ -2692,7 +2721,7 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->add_icon_item(get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP);
menu->set_as_minsize();
- Vector2 popup_pos = get_screen_position() + loop_mode_rect.position + Vector2(0, loop_mode_rect.size.height);
+ Vector2 popup_pos = get_screen_position() + loop_wrap_rect.position + Vector2(0, loop_wrap_rect.size.height);
menu->set_position(popup_pos);
menu->popup();
accept_event();
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 05cf91de1d..2bdc1d4107 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -159,7 +159,7 @@ class AnimationTrackEdit : public Control {
Rect2 update_mode_rect;
Rect2 interp_mode_rect;
- Rect2 loop_mode_rect;
+ Rect2 loop_wrap_rect;
Rect2 remove_rect;
Rect2 bezier_edit_rect;
@@ -466,6 +466,7 @@ class AnimationTrackEditor : public VBoxContainer {
Animation::TrackType track_type = Animation::TrackType::TYPE_ANIMATION;
Animation::InterpolationType interp_type = Animation::InterpolationType::INTERPOLATION_CUBIC;
Animation::UpdateMode update_mode = Animation::UpdateMode::UPDATE_CAPTURE;
+ Animation::LoopMode loop_mode = Animation::LoopMode::LOOP_LINEAR;
bool loop_wrap = false;
bool enabled = false;
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 251e1c2385..5f2f8e91c9 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -45,9 +45,9 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f
String err_str;
if (p_errorexp && p_errorexp[0]) {
- err_str = p_errorexp;
+ err_str = String::utf8(p_errorexp);
} else {
- err_str = String(p_file) + ":" + itos(p_line) + " - " + String(p_error);
+ err_str = String::utf8(p_file) + ":" + itos(p_line) + " - " + String::utf8(p_error);
}
if (p_editor_notify) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index ffbb845fb0..7d550d5c2f 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -174,6 +174,7 @@
#include "editor/plugins/sprite_frames_editor_plugin.h"
#include "editor/plugins/style_box_editor_plugin.h"
#include "editor/plugins/sub_viewport_preview_editor_plugin.h"
+#include "editor/plugins/text_control_editor_plugin.h"
#include "editor/plugins/text_editor.h"
#include "editor/plugins/texture_3d_editor_plugin.h"
#include "editor/plugins/texture_editor_plugin.h"
@@ -1716,8 +1717,10 @@ void EditorNode::_save_scene(String p_file, int idx) {
err = ResourceSaver::save(p_file, sdata, flg);
- _save_external_resources();
+ // This needs to be emitted before saving external resources.
+ emit_signal(SNAME("scene_saved"), p_file);
+ _save_external_resources();
editor_data.save_editor_external_data();
for (Ref<AnimatedValuesBackup> &E : anim_backups) {
@@ -5711,6 +5714,7 @@ void EditorNode::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_help_search"));
ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
+ ADD_SIGNAL(MethodInfo("scene_saved", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("project_settings_changed"));
}
@@ -7010,6 +7014,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(GPUParticlesCollisionSDFEditorPlugin(this)));
add_editor_plugin(memnew(InputEventEditorPlugin(this)));
add_editor_plugin(memnew(SubViewportPreviewEditorPlugin(this)));
+ add_editor_plugin(memnew(TextControlEditorPlugin(this)));
for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {
add_editor_plugin(EditorPlugins::create(i, this));
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 06c39a957c..8e8437f1b2 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -1207,7 +1207,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_shadow_color", "RichTextLabel", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * EDSCALE);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * EDSCALE);
- theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * EDSCALE);
+ theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * EDSCALE);
theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox());
theme->set_stylebox("normal", "RichTextLabel", style_tree_bg);
@@ -1223,7 +1223,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_shadow_color", "Label", Color(0, 0, 0, 0));
theme->set_constant("shadow_offset_x", "Label", 1 * EDSCALE);
theme->set_constant("shadow_offset_y", "Label", 1 * EDSCALE);
- theme->set_constant("shadow_as_outline", "Label", 0 * EDSCALE);
+ theme->set_constant("shadow_outline_size", "Label", 1 * EDSCALE);
theme->set_constant("line_spacing", "Label", 3 * EDSCALE);
// LinkButton
diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp
index 22da12b59b..0d9a546b8e 100644
--- a/editor/editor_toaster.cpp
+++ b/editor/editor_toaster.cpp
@@ -158,11 +158,11 @@ void EditorToaster::_error_handler(void *p_self, const char *p_func, const char
if (p_editor_notify || (show_all_setting == 0 && in_dev) || show_all_setting == 1) {
String err_str;
if (p_errorexp && p_errorexp[0]) {
- err_str = p_errorexp;
+ err_str = String::utf8(p_errorexp);
} else {
- err_str = String(p_error);
+ err_str = String::utf8(p_error);
}
- String tooltip_str = String(p_file) + ":" + itos(p_line);
+ String tooltip_str = String::utf8(p_file) + ":" + itos(p_line);
if (!p_editor_notify) {
if (p_type == ERR_HANDLER_WARNING) {
diff --git a/editor/icons/PingPongLoop.svg b/editor/icons/PingPongLoop.svg
new file mode 100644
index 0000000000..c44f889b49
--- /dev/null
+++ b/editor/icons/PingPongLoop.svg
@@ -0,0 +1 @@
+<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m10 7h-4v-2l-4 3 4 3v-2h4v2l4-3-4-3z"/><path d="m0 1v14h2v-7-7z"/><path d="m14 1v7 7h2v-14z"/></g></svg>
diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp
index d219f6e325..2c1d2149c6 100644
--- a/editor/import/editor_import_plugin.cpp
+++ b/editor/import/editor_import_plugin.cpp
@@ -57,6 +57,7 @@ void EditorImportPlugin::get_recognized_extensions(List<String> *p_extensions) c
for (int i = 0; i < extensions.size(); i++) {
p_extensions->push_back(extensions[i]);
}
+ return;
}
ERR_FAIL_MSG("Unimplemented _get_recognized_extensions in add-on.");
}
@@ -139,6 +140,7 @@ void EditorImportPlugin::get_import_options(List<ResourceImporter::ImportOption>
ImportOption option(PropertyInfo(default_value.get_type(), name, hint, hint_string, usage), default_value);
r_options->push_back(option);
}
+ return;
}
ERR_FAIL_MSG("Unimplemented _get_import_options in add-on.");
diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp
index 2e7ef1402b..a64be54f2d 100644
--- a/editor/import/resource_importer_bmfont.cpp
+++ b/editor/import/resource_importer_bmfont.cpp
@@ -359,6 +359,8 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
int height = 0;
int ascent = 0;
int outline = 0;
+ uint32_t st_flags = 0;
+ String font_name;
bool packed = false;
uint8_t ch[4] = { 0, 0, 0, 0 }; // RGBA
@@ -382,13 +384,23 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
base_size = f->get_16();
uint8_t flags = f->get_8();
ERR_FAIL_COND_V_MSG(flags & 0x02, ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
+ if (flags & (1 << 3)) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ if (flags & (1 << 2)) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
f->get_8(); // non-unicode charset, skip
f->get_16(); // stretch_h, skip
f->get_8(); // aa, skip
f->get_32(); // padding, skip
f->get_16(); // spacing, skip
outline = f->get_8();
- // font name, skip
+ // font name
+ PackedByteArray name_data;
+ name_data.resize(block_size - 14);
+ f->get_buffer(name_data.ptrw(), block_size - 14);
+ font_name = String::utf8((const char *)name_data.ptr(), block_size - 14);
font->set_fixed_size(base_size);
} break;
case 2: /* common */ {
@@ -601,6 +613,19 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
if (keys.has("outline")) {
outline = keys["outline"].to_int();
}
+ if (keys.has("bold")) {
+ if (keys["bold"].to_int()) {
+ st_flags |= TextServer::FONT_BOLD;
+ }
+ }
+ if (keys.has("italic")) {
+ if (keys["italic"].to_int()) {
+ st_flags |= TextServer::FONT_ITALIC;
+ }
+ }
+ if (keys.has("face")) {
+ font_name = keys["face"];
+ }
ERR_FAIL_COND_V_MSG((!keys.has("unicode") || keys["unicode"].to_int() != 1), ERR_CANT_CREATE, TTR("Non-unicode version of BMFont is not supported."));
} else if (type == "common") {
if (keys.has("lineHeight")) {
@@ -778,6 +803,8 @@ Error ResourceImporterBMFont::import(const String &p_source_file, const String &
}
}
+ font->set_font_name(font_name);
+ font->set_font_style(st_flags);
font->set_ascent(0, base_size, ascent);
font->set_descent(0, base_size, height - ascent);
diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/resource_importer_obj.cpp
index 01af75c338..8eab27b533 100644
--- a/editor/import/resource_importer_obj.cpp
+++ b/editor/import/resource_importer_obj.cpp
@@ -405,11 +405,11 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
current_material_library = l.replace("mtllib", "").strip_edges();
if (!material_map.has(current_material_library)) {
Map<String, Ref<StandardMaterial3D>> lib;
- Error err = _parse_material_library(current_material_library, lib, r_missing_deps);
- if (err == ERR_CANT_OPEN) {
- String dir = p_path.get_base_dir();
- err = _parse_material_library(dir.plus_file(current_material_library), lib, r_missing_deps);
+ String lib_path = current_material_library;
+ if (lib_path.is_relative_path()) {
+ lib_path = p_path.get_base_dir().plus_file(current_material_library);
}
+ Error err = _parse_material_library(lib_path, lib, r_missing_deps);
if (err == OK) {
material_map[current_material_library] = lib;
}
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 5efb08b95d..11639314d6 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -424,10 +424,10 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, Map<Ref<I
String animname = E;
const int loop_string_count = 3;
- static const char *loop_strings[loop_string_count] = { "loops", "loop", "cycle" };
+ static const char *loop_strings[loop_string_count] = { "loop_mode", "loop", "cycle" };
for (int i = 0; i < loop_string_count; i++) {
if (_teststr(animname, loop_strings[i])) {
- anim->set_loop(true);
+ anim->set_loop_mode(Animation::LoopMode::LOOP_LINEAR);
animname = _fixstr(animname, loop_strings[i]);
ap->rename_animation(E, animname);
}
@@ -868,7 +868,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
String name = node_settings["clip_" + itos(i + 1) + "/name"];
int from_frame = node_settings["clip_" + itos(i + 1) + "/start_frame"];
int end_frame = node_settings["clip_" + itos(i + 1) + "/end_frame"];
- bool loop = node_settings["clip_" + itos(i + 1) + "/loops"];
+ Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)node_settings["clip_" + itos(i + 1) + "/loop_mode"]);
bool save_to_file = node_settings["clip_" + itos(i + 1) + "/save_to_file/enabled"];
bool save_to_path = node_settings["clip_" + itos(i + 1) + "/save_to_file/path"];
bool save_to_file_keep_custom = node_settings["clip_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"];
@@ -876,7 +876,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
animation_clips.push_back(name);
animation_clips.push_back(from_frame / p_animation_fps);
animation_clips.push_back(end_frame / p_animation_fps);
- animation_clips.push_back(loop);
+ animation_clips.push_back(loop_mode);
animation_clips.push_back(save_to_file);
animation_clips.push_back(save_to_path);
animation_clips.push_back(save_to_file_keep_custom);
@@ -903,7 +903,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
- anim->set_loop(anim_settings["settings/loops"]);
+ anim->set_loop_mode(static_cast<Animation::LoopMode>((int)anim_settings["settings/loop_mode"]));
bool save = anim_settings["save_to_file/enabled"];
String path = anim_settings["save_to_file/path"];
bool keep_custom = anim_settings["save_to_file/keep_custom_tracks"];
@@ -977,7 +977,7 @@ Ref<Animation> ResourceImporterScene::_save_animation_to_file(Ref<Animation> ani
old_anim->copy_track(i, anim);
}
}
- anim->set_loop(old_anim->has_loop());
+ anim->set_loop_mode(old_anim->get_loop_mode());
}
}
@@ -1005,7 +1005,7 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
String name = p_clips[i];
float from = p_clips[i + 1];
float to = p_clips[i + 2];
- bool loop = p_clips[i + 3];
+ Animation::LoopMode loop_mode = static_cast<Animation::LoopMode>((int)p_clips[i + 3]);
bool save_to_file = p_clips[i + 4];
String save_to_path = p_clips[i + 5];
bool keep_current = p_clips[i + 6];
@@ -1135,7 +1135,7 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_
}
}
- new_anim->set_loop(loop);
+ new_anim->set_loop_mode(loop_mode);
new_anim->set_length(to - from);
anim->add_animation(name, new_anim);
@@ -1218,7 +1218,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "use_external/path", PROPERTY_HINT_FILE, "*.material,*.res,*.tres"), ""));
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION: {
- r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "settings/loops"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "settings/loop_mode"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "save_to_file/path", PROPERTY_HINT_SAVE_FILE, "*.res,*.tres"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "save_to_file/keep_custom_tracks"), ""));
@@ -1240,7 +1240,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/name"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/start_frame"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/end_frame"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/loops"), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "slice_" + itos(i + 1) + "/loop_mode"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "slice_" + itos(i + 1) + "/save_to_file/path", PROPERTY_HINT_SAVE_FILE, ".res,*.tres"), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));
@@ -1428,7 +1428,7 @@ void ResourceImporterScene::get_import_options(List<ImportOption> *r_options, in
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "meshes/lightmap_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.1));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
- r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 15));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index 89383d3dde..877bdd50fb 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -272,7 +272,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
if (loop_type == 0x00) {
loop = AudioStreamSample::LOOP_FORWARD;
} else if (loop_type == 0x01) {
- loop = AudioStreamSample::LOOP_PING_PONG;
+ loop = AudioStreamSample::LOOP_PINGPONG;
} else if (loop_type == 0x02) {
loop = AudioStreamSample::LOOP_BACKWARD;
}
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index ea025dad3e..65238446f9 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
#include "scene/main/window.h"
+#include "scene/resources/animation.h"
#include "servers/rendering_server.h"
void AnimationPlayerEditor::_node_removed(Node *p_node) {
@@ -72,7 +73,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
if (player->has_animation(animname)) {
Ref<Animation> anim = player->get_animation(animname);
if (!anim.is_null()) {
- frame->set_max(anim->get_length());
+ frame->set_max((double)anim->get_length());
}
}
}
@@ -289,7 +290,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
track_editor->set_root(root);
}
}
- frame->set_max(anim->get_length());
+ frame->set_max((double)anim->get_length());
} else {
track_editor->set_animation(Ref<Animation>());
@@ -1014,7 +1015,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool
Ref<Animation> anim;
anim = player->get_animation(current);
- float pos = CLAMP(anim->get_length() * (p_value / frame->get_max()), 0, anim->get_length());
+ float pos = CLAMP((double)anim->get_length() * (p_value / frame->get_max()), 0, (double)anim->get_length());
if (track_editor->is_snap_enabled()) {
pos = Math::snapped(pos, _get_editor_step());
}
@@ -1424,7 +1425,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
float pos = cpos + step_off * anim->get_step();
- bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length());
+ bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
onion.captures_valid.write[cidx] = valid;
if (valid) {
player->seek(pos, true);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 376c7d3f07..061483decf 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -6192,14 +6192,14 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
label = memnew(Label);
label->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 1));
- label->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
+ label->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label->hide();
canvas_item_editor->get_controls_container()->add_child(label);
label_desc = memnew(Label);
label_desc->add_theme_color_override("font_color", Color(0.6f, 0.6f, 0.6f, 1));
label_desc->add_theme_color_override("font_shadow_color", Color(0.2f, 0.2f, 0.2f, 1));
- label_desc->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
+ label_desc->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label_desc->add_theme_constant_override("line_spacing", 0);
label_desc->hide();
canvas_item_editor->get_controls_container()->add_child(label_desc);
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index fa5381bb10..5b1cbe6f00 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -2245,12 +2245,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
_menu_option(VIEW_RIGHT);
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
- cursor.x_rot -= Math_PI / 12.0;
+ // Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
+ cursor.x_rot = CLAMP(cursor.x_rot - Math_PI / 12.0, -1.57, 1.57);
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
- cursor.x_rot += Math_PI / 12.0;
+ // Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
+ cursor.x_rot = CLAMP(cursor.x_rot + Math_PI / 12.0, -1.57, 1.57);
view_type = VIEW_TYPE_USER;
_update_name();
}
@@ -2885,13 +2887,13 @@ void Node3DEditorViewport::_notification(int p_what) {
// Color labels depending on performance level ("good" = green, "OK" = yellow, "bad" = red).
// Middle point is at 15 ms.
- cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(1)));
+ cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(2)));
cpu_time_label->add_theme_color_override(
"font_color",
frame_time_gradient->get_color_at_offset(
Math::range_lerp(cpu_time, 0, 30, 0, 1)));
- gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(1)));
+ gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(2)));
// Middle point is at 15 ms.
gpu_time_label->add_theme_color_override(
"font_color",
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index aad378ecec..000dfe8bda 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -989,10 +989,6 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
RES script = se->get_edited_resource();
- if (script->is_built_in()) {
- continue; //internal script, who cares
- }
-
if (script == p_res) {
se->tag_saved_version();
}
@@ -1002,6 +998,31 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
_trigger_live_script_reload();
}
+void ScriptEditor::_scene_saved_callback(const String &p_path) {
+ // If scene was saved, mark all built-in scripts from that scene as saved.
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (!se) {
+ continue;
+ }
+
+ RES edited_res = se->get_edited_resource();
+
+ if (!edited_res->is_built_in()) {
+ continue; // External script, who cares.
+ }
+
+ if (edited_res->get_path().get_slice("::", 0) == p_path) {
+ se->tag_saved_version();
+ }
+
+ Ref<Script> scr = edited_res;
+ if (scr.is_valid() && scr->is_tool()) {
+ scr->reload(true);
+ }
+ }
+}
+
void ScriptEditor::_trigger_live_script_reload() {
if (!pending_auto_reload && auto_reload_running_scripts) {
call_deferred(SNAME("_live_auto_reload_running_scripts"));
@@ -1525,6 +1546,7 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
+ editor->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
editor->get_filesystem_dock()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
@@ -1619,7 +1641,7 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
}
if (script->is_built_in() && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
- _close_tab(i);
+ _close_tab(i, false);
i--;
}
}
@@ -1926,20 +1948,7 @@ void ScriptEditor::_update_script_names() {
// to update original path to previously edited resource.
se->set_meta("_edit_res_path", path);
}
- bool built_in = !path.is_resource_file();
- String name;
-
- if (built_in) {
- name = path.get_file();
- const String &resource_name = se->get_edited_resource()->get_name();
- if (resource_name != "") {
- // If the built-in script has a custom resource name defined,
- // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
- name = vformat("%s (%s)", resource_name, name.substr(0, name.find("::", 0)));
- }
- } else {
- name = se->get_name();
- }
+ String name = se->get_name();
_ScriptEditorItemData sd;
sd.icon = icon;
@@ -2403,7 +2412,17 @@ void ScriptEditor::save_current_script() {
}
}
- editor->save_resource(resource);
+ if (resource->is_built_in()) {
+ // If built-in script, save the scene instead.
+ const String scene_path = resource->get_path().get_slice("::", 0);
+ if (!scene_path.is_empty()) {
+ Vector<String> scene_to_save;
+ scene_to_save.push_back(scene_path);
+ editor->save_scene_list(scene_to_save);
+ }
+ } else {
+ editor->save_resource(resource);
+ }
if (script != nullptr) {
const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
@@ -2416,6 +2435,8 @@ void ScriptEditor::save_current_script() {
}
void ScriptEditor::save_all_scripts() {
+ Vector<String> scenes_to_save;
+
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
@@ -2474,9 +2495,19 @@ void ScriptEditor::save_all_scripts() {
update_doc(doc.name);
}
}
+ } else {
+ // For built-in scripts, save their scenes instead.
+ const String scene_path = edited_res->get_path().get_slice("::", 0);
+ if (!scenes_to_save.has(scene_path)) {
+ scenes_to_save.push_back(scene_path);
+ }
}
}
+ if (!scenes_to_save.is_empty()) {
+ editor->save_scene_list(scenes_to_save);
+ }
+
_update_script_names();
EditorFileSystem::get_singleton()->update_script_classes();
}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 2b0bdfd109..0adeca031e 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -365,6 +365,7 @@ class ScriptEditor : public PanelContainer {
void _add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args);
void _res_saved_callback(const Ref<Resource> &p_res);
+ void _scene_saved_callback(const String &p_path);
bool open_textfile_after_create = true;
bool trim_trailing_whitespace_on_save;
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 24cdc06d78..a655420d27 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -375,18 +375,21 @@ void ScriptTextEditor::ensure_focus() {
String ScriptTextEditor::get_name() {
String name;
- if (!script->is_built_in()) {
- name = script->get_path().get_file();
- if (is_unsaved()) {
- if (script->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = script->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in scripts before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (script->is_built_in()) {
+ const String &script_name = script->get_name();
+ if (script_name != "") {
+ // If the built-in script has a custom resource name defined,
+ // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
- } else if (script->get_name() != "") {
- name = script->get_name();
- } else {
- name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
diff --git a/editor/plugins/text_control_editor_plugin.cpp b/editor/plugins/text_control_editor_plugin.cpp
new file mode 100644
index 0000000000..c878c83430
--- /dev/null
+++ b/editor/plugins/text_control_editor_plugin.cpp
@@ -0,0 +1,375 @@
+/*************************************************************************/
+/* text_control_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "text_control_editor_plugin.h"
+
+#include "editor/editor_scale.h"
+
+void TextControlEditor::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts), make_binds(""));
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ clear_formatting->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
+ EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts));
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+void TextControlEditor::_find_resources(EditorFileSystemDirectory *p_dir) {
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _find_resources(p_dir->get_subdir(i));
+ }
+
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ if (p_dir->get_file_type(i) == "FontData") {
+ Ref<FontData> fd = ResourceLoader::load(p_dir->get_file_path(i));
+ if (fd.is_valid()) {
+ String name = fd->get_font_name();
+ String sty = fd->get_font_style_name();
+ if (sty.is_empty()) {
+ sty = "Default";
+ }
+ fonts[name][sty] = p_dir->get_file_path(i);
+ }
+ }
+ }
+}
+
+void TextControlEditor::_reload_fonts(const String &p_path) {
+ fonts.clear();
+ _find_resources(EditorFileSystem::get_singleton()->get_filesystem());
+ _update_control();
+}
+
+void TextControlEditor::_update_fonts_menu() {
+ font_list->clear();
+ font_list->add_item(TTR("[Theme Default]"), FONT_INFO_THEME_DEFAULT);
+ if (custom_font.is_valid()) {
+ font_list->add_item(TTR("[Custom Font]"), FONT_INFO_USER_CUSTOM);
+ }
+
+ int id = FONT_INFO_ID;
+ for (Map<String, Map<String, String>>::Element *E = fonts.front(); E; E = E->next()) {
+ font_list->add_item(E->key(), id++);
+ }
+
+ if (font_list->get_item_count() > 1) {
+ font_list->show();
+ } else {
+ font_list->hide();
+ }
+}
+
+void TextControlEditor::_update_styles_menu() {
+ font_style_list->clear();
+ if ((font_list->get_selected_id() >= FONT_INFO_ID)) {
+ const String &name = font_list->get_item_text(font_list->get_selected());
+ for (Map<String, String>::Element *E = fonts[name].front(); E; E = E->next()) {
+ font_style_list->add_item(E->key());
+ }
+ } else {
+ font_style_list->add_item("Default");
+ }
+
+ if (font_style_list->get_item_count() > 1) {
+ font_style_list->show();
+ } else {
+ font_style_list->hide();
+ }
+}
+
+void TextControlEditor::_update_control() {
+ if (edited_control) {
+ // Get override names.
+ if (edited_control->is_class("RichTextLabel")) {
+ edited_color = "default_color";
+ edited_font = "normal_font";
+ edited_font_size = "normal_font_size";
+ } else {
+ edited_color = "font_color";
+ edited_font = "font";
+ edited_font_size = "font_size";
+ }
+
+ // Get font override.
+ Ref<Font> font;
+ if (edited_control->has_theme_font_override(edited_font)) {
+ font = edited_control->get_theme_font(edited_font);
+ }
+ if (font.is_valid()) {
+ if (font->get_data_count() != 1) {
+ // Composite font, save it to "custom_font" to allow undoing font change.
+ custom_font = font;
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_USER_CUSTOM);
+ _update_styles_menu();
+ font_style_list->select(0);
+ } else {
+ // Single face font, search for the font with matching name and style.
+ String name = font->get_data(0)->get_font_name();
+ String style = font->get_data(0)->get_font_style_name();
+ if (fonts.has(name) && fonts[name].has(style)) {
+ _update_fonts_menu();
+ for (int i = 0; i < font_list->get_item_count(); i++) {
+ if (font_list->get_item_text(i) == name) {
+ font_list->select(i);
+ break;
+ }
+ }
+ _update_styles_menu();
+ for (int i = 0; i < font_style_list->get_item_count(); i++) {
+ if (font_style_list->get_item_text(i) == style) {
+ font_style_list->select(i);
+ break;
+ }
+ }
+ } else {
+ // Unknown font, save it to "custom_font" to allow undoing font change.
+ custom_font = font;
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_USER_CUSTOM);
+ _update_styles_menu();
+ font_style_list->select(0);
+ }
+ }
+ } else {
+ // No font override, select "Theme Default".
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_THEME_DEFAULT);
+ _update_styles_menu();
+ font_style_list->select(0);
+ }
+
+ // Get other theme overrides.
+ font_size_list->set_value(edited_control->get_theme_font_size(edited_font_size));
+ outline_size_list->set_value(edited_control->get_theme_constant("outline_size"));
+
+ font_color_picker->set_pick_color(edited_control->get_theme_color(edited_color));
+ outline_color_picker->set_pick_color(edited_control->get_theme_color("font_outline_color"));
+ }
+}
+
+void TextControlEditor::_font_selected(int p_id) {
+ _update_styles_menu();
+ _set_font();
+}
+
+void TextControlEditor::_font_style_selected(int p_id) {
+ _set_font();
+}
+
+void TextControlEditor::_set_font() {
+ if (edited_control) {
+ if (font_list->get_selected_id() == FONT_INFO_THEME_DEFAULT) {
+ // Remove font override.
+ edited_control->remove_theme_font_override(edited_font);
+ return;
+ } else if (font_list->get_selected_id() == FONT_INFO_USER_CUSTOM) {
+ // Restore "custom_font".
+ edited_control->add_theme_font_override(edited_font, custom_font);
+ return;
+ } else {
+ // Load new font resource using selected name and style.
+ String name = font_list->get_item_text(font_list->get_selected());
+ String sty = font_style_list->get_item_text(font_style_list->get_selected());
+ if (sty.is_empty()) {
+ sty = "Default";
+ }
+ if (fonts.has(name)) {
+ Ref<FontData> fd = ResourceLoader::load(fonts[name][sty]);
+ if (fd.is_valid()) {
+ Ref<Font> f;
+ f.instantiate();
+ f->add_data(fd);
+ edited_control->add_theme_font_override(edited_font, f);
+ }
+ }
+ }
+ }
+}
+
+void TextControlEditor::_font_size_selected(double p_size) {
+ if (edited_control) {
+ edited_control->add_theme_font_size_override(edited_font_size, p_size);
+ }
+}
+
+void TextControlEditor::_outline_size_selected(double p_size) {
+ if (edited_control) {
+ edited_control->add_theme_constant_override("outline_size", p_size);
+ }
+}
+
+void TextControlEditor::_font_color_changed(const Color &p_color) {
+ if (edited_control) {
+ edited_control->add_theme_color_override(edited_color, p_color);
+ }
+}
+
+void TextControlEditor::_outline_color_changed(const Color &p_color) {
+ if (edited_control) {
+ edited_control->add_theme_color_override("font_outline_color", p_color);
+ }
+}
+
+void TextControlEditor::_clear_formatting() {
+ if (edited_control) {
+ edited_control->begin_bulk_theme_override();
+ edited_control->remove_theme_font_override(edited_font);
+ edited_control->remove_theme_font_size_override(edited_font_size);
+ edited_control->remove_theme_color_override(edited_color);
+ edited_control->remove_theme_color_override("font_outline_color");
+ edited_control->remove_theme_constant_override("outline_size");
+ edited_control->end_bulk_theme_override();
+ _update_control();
+ }
+}
+
+void TextControlEditor::edit(Object *p_object) {
+ Control *ctrl = Object::cast_to<Control>(p_object);
+ if (!ctrl) {
+ edited_control = nullptr;
+ custom_font = Ref<Font>();
+ } else {
+ edited_control = ctrl;
+ custom_font = Ref<Font>();
+ _update_control();
+ }
+}
+
+bool TextControlEditor::handles(Object *p_object) const {
+ Control *ctrl = Object::cast_to<Control>(p_object);
+ if (!ctrl) {
+ return false;
+ } else {
+ bool valid = false;
+ ctrl->get("text", &valid);
+ return valid;
+ }
+}
+
+TextControlEditor::TextControlEditor() {
+ add_child(memnew(VSeparator));
+
+ font_list = memnew(OptionButton);
+ font_list->set_flat(true);
+ font_list->set_tooltip(TTR("Font"));
+ add_child(font_list);
+ font_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_selected));
+
+ font_style_list = memnew(OptionButton);
+ font_style_list->set_flat(true);
+ font_style_list->set_tooltip(TTR("Font style"));
+ font_style_list->set_toggle_mode(true);
+ add_child(font_style_list);
+ font_style_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_style_selected));
+
+ font_size_list = memnew(SpinBox);
+ font_size_list->set_tooltip(TTR("Font Size"));
+ font_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ font_size_list->set_min(6);
+ font_size_list->set_step(1);
+ font_size_list->set_max(96);
+ font_size_list->get_line_edit()->set_flat(true);
+ add_child(font_size_list);
+ font_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_font_size_selected));
+
+ font_color_picker = memnew(ColorPickerButton);
+ font_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
+ font_color_picker->set_flat(true);
+ font_color_picker->set_tooltip(TTR("Text Color"));
+ add_child(font_color_picker);
+ font_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_font_color_changed));
+
+ add_child(memnew(VSeparator));
+
+ outline_size_list = memnew(SpinBox);
+ outline_size_list->set_tooltip(TTR("Outline Size"));
+ outline_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ outline_size_list->set_min(0);
+ outline_size_list->set_step(1);
+ outline_size_list->set_max(96);
+ outline_size_list->get_line_edit()->set_flat(true);
+ add_child(outline_size_list);
+ outline_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_outline_size_selected));
+
+ outline_color_picker = memnew(ColorPickerButton);
+ outline_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
+ outline_color_picker->set_flat(true);
+ outline_color_picker->set_tooltip(TTR("Outline Color"));
+ add_child(outline_color_picker);
+ outline_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_outline_color_changed));
+
+ add_child(memnew(VSeparator));
+
+ clear_formatting = memnew(Button);
+ clear_formatting->set_flat(true);
+ clear_formatting->set_tooltip(TTR("Clear Formatting"));
+ add_child(clear_formatting);
+ clear_formatting->connect("pressed", callable_mp(this, &TextControlEditor::_clear_formatting));
+}
+
+/*************************************************************************/
+
+void TextControlEditorPlugin::edit(Object *p_object) {
+ text_ctl_editor->edit(p_object);
+}
+
+bool TextControlEditorPlugin::handles(Object *p_object) const {
+ return text_ctl_editor->handles(p_object);
+}
+
+void TextControlEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ text_ctl_editor->show();
+ } else {
+ text_ctl_editor->hide();
+ text_ctl_editor->edit(nullptr);
+ }
+}
+
+TextControlEditorPlugin::TextControlEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ text_ctl_editor = memnew(TextControlEditor);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(text_ctl_editor);
+
+ text_ctl_editor->hide();
+}
diff --git a/editor/plugins/text_control_editor_plugin.h b/editor/plugins/text_control_editor_plugin.h
new file mode 100644
index 0000000000..7f4aa3754c
--- /dev/null
+++ b/editor/plugins/text_control_editor_plugin.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* text_control_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 TEXT_CONTROL_EDITOR_PLUGIN_H
+#define TEXT_CONTROL_EDITOR_PLUGIN_H
+
+#include "canvas_item_editor_plugin.h"
+#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/gui/color_rect.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/popup_menu.h"
+
+/*************************************************************************/
+
+class TextControlEditor : public HBoxContainer {
+ GDCLASS(TextControlEditor, HBoxContainer);
+
+ enum FontInfoID {
+ FONT_INFO_THEME_DEFAULT = 0,
+ FONT_INFO_USER_CUSTOM = 1,
+ FONT_INFO_ID = 100,
+ };
+
+ Map<String, Map<String, String>> fonts;
+
+ OptionButton *font_list = nullptr;
+ SpinBox *font_size_list = nullptr;
+ OptionButton *font_style_list = nullptr;
+ ColorPickerButton *font_color_picker = nullptr;
+ SpinBox *outline_size_list = nullptr;
+ ColorPickerButton *outline_color_picker = nullptr;
+ Button *clear_formatting = nullptr;
+
+ Control *edited_control = nullptr;
+ String edited_color;
+ String edited_font;
+ String edited_font_size;
+ Ref<Font> custom_font;
+
+protected:
+ void _notification(int p_notification);
+ static void _bind_methods(){};
+
+ void _find_resources(EditorFileSystemDirectory *p_dir);
+ void _reload_fonts(const String &p_path);
+
+ void _update_fonts_menu();
+ void _update_styles_menu();
+ void _update_control();
+
+ void _font_selected(int p_id);
+ void _font_style_selected(int p_id);
+ void _set_font();
+
+ void _font_size_selected(double p_size);
+ void _outline_size_selected(double p_size);
+
+ void _font_color_changed(const Color &p_color);
+ void _outline_color_changed(const Color &p_color);
+
+ void _clear_formatting();
+
+public:
+ void edit(Object *p_object);
+ bool handles(Object *p_object) const;
+
+ TextControlEditor();
+};
+
+/*************************************************************************/
+
+class TextControlEditorPlugin : public EditorPlugin {
+ GDCLASS(TextControlEditorPlugin, EditorPlugin);
+
+ TextControlEditor *text_ctl_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const override { return "TextControlFontEditor"; }
+ bool has_main_screen() const override { return false; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+ virtual void make_visible(bool p_visible) override;
+
+ TextControlEditorPlugin(EditorNode *p_node);
+};
+
+#endif // TEXT_CONTROL_EDITOR_PLUGIN_H
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 3b45f32927..8186c2c84c 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -65,18 +65,21 @@ void TextEditor::_load_theme_settings() {
String TextEditor::get_name() {
String name;
- if (!text_file->is_built_in()) {
- name = text_file->get_path().get_file();
- if (is_unsaved()) {
- if (text_file->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = text_file->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in text_files before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (text_file->is_built_in()) {
+ const String &text_file_name = text_file->get_name();
+ if (text_file_name != "") {
+ // If the built-in text_file has a custom resource name defined,
+ // display the built-in text_file name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", text_file_name, name.get_slice("::", 0));
}
- } else if (text_file->get_name() != "") {
- name = text_file->get_name();
- } else {
- name = text_file->get_class() + "(" + itos(text_file->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp
index bd1923f4ab..b4e394a1c0 100644
--- a/editor/plugins/texture_3d_editor_plugin.cpp
+++ b/editor/plugins/texture_3d_editor_plugin.cpp
@@ -173,7 +173,7 @@ Texture3DEditor::Texture3DEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
- info->add_theme_constant_override("shadow_as_outline", 1);
+ info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index b9ec6bf5ab..e25b0270b4 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -101,7 +101,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
metadata_label->add_theme_color_override("font_outline_color", Color::named("black"));
metadata_label->add_theme_constant_override("outline_size", 2 * EDSCALE);
- metadata_label->add_theme_constant_override("shadow_as_outline", 1);
+ metadata_label->add_theme_constant_override("shadow_outline_size", 1);
metadata_label->set_h_size_flags(Control::SIZE_SHRINK_END);
metadata_label->set_v_size_flags(Control::SIZE_SHRINK_END);
diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp
index 424e018a47..ee62138d12 100644
--- a/editor/plugins/texture_layered_editor_plugin.cpp
+++ b/editor/plugins/texture_layered_editor_plugin.cpp
@@ -249,7 +249,7 @@ TextureLayeredEditor::TextureLayeredEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
- info->add_theme_constant_override("shadow_as_outline", 1);
+ info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);
diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp
index 2792c193d9..f5b70504fa 100644
--- a/editor/rename_dialog.cpp
+++ b/editor/rename_dialog.cpp
@@ -461,7 +461,7 @@ String RenameDialog::_substitute(const String &subject, const Node *node, int co
void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) {
RenameDialog *self = (RenameDialog *)p_self;
- String source_file(p_file);
+ String source_file = String::utf8(p_file);
// Only show first error that is related to "regex"
if (self->has_errors || source_file.find("regex") < 0) {
@@ -470,9 +470,9 @@ void RenameDialog::_error_handler(void *p_self, const char *p_func, const char *
String err_str;
if (p_errorexp && p_errorexp[0]) {
- err_str = p_errorexp;
+ err_str = String::utf8(p_errorexp);
} else {
- err_str = p_error;
+ err_str = String::utf8(p_error);
}
self->has_errors = true;
diff --git a/main/main.cpp b/main/main.cpp
index 863ffccfae..7355493677 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -2050,9 +2050,7 @@ bool Main::start() {
GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
GLOBAL_DEF("mono/profiler/args", "log:calls,alloc,sample,output=output.mlpd");
GLOBAL_DEF("mono/profiler/enabled", false);
- GLOBAL_DEF("mono/unhandled_exception_policy", 0);
- // From editor/csharp_project.cpp.
- GLOBAL_DEF("mono/project/auto_update_project", true);
+ GLOBAL_DEF("mono/runtime/unhandled_exception_policy", 0);
#endif
DocTools doc;
@@ -2689,10 +2687,10 @@ bool Main::iteration() {
if (frame > 1000000) {
if (editor || project_manager) {
if (print_fps) {
- print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1)));
+ print_line(vformat("Editor FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
} else if (GLOBAL_GET("debug/settings/stdout/print_fps") || print_fps) {
- print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(1)));
+ print_line(vformat("Project FPS: %d (%s mspf)", frames, rtos(1000.0 / frames).pad_decimals(2)));
}
Engine::get_singleton()->_fps = frames;
diff --git a/misc/dist/osx_tools.app/Contents/Resources/af.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/af.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/af.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ar.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ar.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ar.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/az.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/az.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/az.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/bg.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/bg.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/bg.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/bn.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/bn.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/bn.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/br.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/br.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/br.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ca.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ca.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ca.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/cs.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/cs.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/cs.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/da.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/da.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/da.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/de.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/de.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/de.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/el.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/el.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/el.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/eo.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/eo.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/eo.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/es.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/es.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/es.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/es_AR.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/es_AR.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/es_AR.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/et.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/et.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/et.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/eu.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/eu.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/eu.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/fa.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/fa.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/fa.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/fi.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/fi.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/fi.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/fil.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/fil.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/fil.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/fr.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/fr.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/fr.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ga.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ga.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ga.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/gl.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/gl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/gl.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/he.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/he.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/he.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/hi.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/hi.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/hi.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/hr.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/hr.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/hr.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/hu.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/hu.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/hu.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/id.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/id.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/id.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/is.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/is.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/is.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/it.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/it.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/it.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ja.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ja.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ja.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ka.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ka.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ka.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/km.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/km.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/km.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ko.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ko.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ko.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/lt.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/lt.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/lt.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/lv.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/lv.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/lv.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/mi.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/mi.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/mi.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/mk.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/mk.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/mk.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ml.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ml.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ml.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/mr.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/mr.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/mr.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ms.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ms.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ms.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/nb.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/nb.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/nb.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/nl.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/nl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/nl.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/or.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/or.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/or.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/pl.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/pl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/pl.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/pt.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/pt.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/pt.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/pt_BR.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/pt_BR.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/pt_BR.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ro.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ro.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ro.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ru.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ru.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ru.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/si.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/si.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/si.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sk.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sk.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sk.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sl.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sl.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sq.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sq.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sq.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sr-Cyrl.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sr-Cyrl.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sr-Cyrl.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sr-Latn.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sr-Latn.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sr-Latn.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/sv.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/sv.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/sv.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ta.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ta.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ta.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/te.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/te.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/te.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/th.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/th.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/th.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/tr.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/tr.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/tr.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/tt.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/tt.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/tt.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/tzm.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/tzm.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/tzm.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/uk.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/uk.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/uk.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/ur_PK.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/ur_PK.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/ur_PK.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/vi.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/vi.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/vi.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/zh_CN.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/zh_CN.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/zh_CN.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/zh_HK.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/zh_HK.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/zh_HK.lproj/InfoPlist.strings
diff --git a/misc/dist/osx_tools.app/Contents/Resources/zh_TW.lproj/InfoPlist.strings b/misc/dist/osx_tools.app/Contents/Resources/zh_TW.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/misc/dist/osx_tools.app/Contents/Resources/zh_TW.lproj/InfoPlist.strings
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 610d2145cd..684a20cf4d 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -837,8 +837,17 @@ void BulletPhysicsServer3D::body_set_ray_pickable(RID p_body, bool p_enable) {
}
PhysicsDirectBodyState3D *BulletPhysicsServer3D::body_get_direct_state(RID p_body) {
+ if (!rigid_body_owner.owns(p_body)) {
+ return nullptr;
+ }
+
RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, nullptr);
+
+ if (!body->get_space()) {
+ return nullptr;
+ }
+
return BulletPhysicsDirectBodyState3D::get_singleton(body);
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index b96139ac51..bde6783322 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -740,10 +740,24 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
#endif // TOOLS_ENABLED
if (member->identifier != nullptr) {
- // Enums may be unnamed.
- // TODO: Consider names in outer scope too, for constants and classes (and static functions?)
- if (current_class->members_indices.has(member->identifier->name)) {
- push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == member->identifier->name) {
+ push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ return;
+ }
+ }
+ if (current_class->members_indices.has(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ } else if (Variant::has_utility_function(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ } else if (ClassDB::class_exists(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a global class.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ } else {
+ current_class->add_member(member);
+ }
} else {
current_class->add_member(member);
}
@@ -811,9 +825,27 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
return nullptr;
}
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Local var "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+
VariableNode *variable = alloc_node<VariableNode>();
- variable->identifier = parse_identifier();
- variable->export_info.name = variable->identifier->name;
+ variable->identifier = identifier;
+ variable->export_info.name = identifier->name;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check(GDScriptTokenizer::Token::NEWLINE)) {
@@ -1066,8 +1098,26 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
return nullptr;
}
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+
ParameterNode *parameter = alloc_node<ParameterNode>();
- parameter->identifier = parse_identifier();
+ parameter->identifier = identifier;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check((GDScriptTokenizer::Token::EQUAL))) {
@@ -1145,13 +1195,31 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
HashMap<StringName, int> elements;
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+
do {
if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
break; // Allow trailing comma.
}
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
EnumNode::Value item;
- item.identifier = parse_identifier();
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+ item.identifier = identifier;
item.parent_enum = enum_node;
item.line = previous.start_line;
item.leftmost_column = previous.leftmost_column;
@@ -2774,6 +2842,23 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
get_node->chain.push_back(identifier);
} while (match(GDScriptTokenizer::Token::SLASH));
return get_node;
+ } else if (match(GDScriptTokenizer::Token::SLASH)) {
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+ IdentifierNode *identifier_root = alloc_node<IdentifierNode>();
+ get_node->chain.push_back(identifier_root);
+ int chain_position = 0;
+ do {
+ make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
+ if (!current.is_node_name()) {
+ push_error(R"(Expect node path after "/".)");
+ return nullptr;
+ }
+ advance();
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
+ get_node->chain.push_back(identifier);
+ } while (match(GDScriptTokenizer::Token::SLASH));
+ return get_node;
} else {
push_error(R"(Expect node path as string or identifier after "$".)");
return nullptr;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index a1cc2246d6..6dd8c3e0dd 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -3309,7 +3309,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
int err_line = line;
if (err_text == "") {
- err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
+ err_text = "Internal script error! Opcode: " + itos(last_opcode) + " (please report).";
}
if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index ced80e26b5..0b2854e731 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -5856,7 +5856,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
animation->set_name(name);
if (anim->get_loop()) {
- animation->set_loop(true);
+ animation->set_loop_mode(Animation::LOOP_LINEAR);
}
float length = 0.0;
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index abd860a55f..2bca8bc24e 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -12,13 +12,10 @@
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <return type="Variant" />
<description>
Returns a new instance of the script.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 417f8ac704..a148072245 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -11,66 +11,55 @@
</tutorials>
<methods>
<method name="attach_thread">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the current MonoDomain ID.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="get_scripts_domain_id">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="is_domain_finalizing_for_unload">
- <return type="bool">
- </return>
- <argument index="0" name="domain_id" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="domain_id" type="int" />
<description>
Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_shutting_down">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
</description>
</method>
<method name="is_scripts_domain_loaded">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 6f7fac7429..fbc8ff64a6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -693,5 +693,23 @@ namespace Godot
}
return min + ((((value - min) % range) + range) % range);
}
+
+ private static real_t Fract(real_t value)
+ {
+ return value - (real_t)Math.Floor(value);
+ }
+
+ /// <summary>
+ /// Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code].
+ /// If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave).
+ /// If [code]length[/code] is less than zero, it becomes positive.
+ /// </summary>
+ /// <param name="value">The value to pingpong.</param>
+ /// <param name="length">The maximum value of the function.</param>
+ /// <returns>The ping-ponged value.</returns>
+ public static real_t PingPong(real_t value, real_t length)
+ {
+ return (length != (real_t)0.0) ? Abs(Fract((value - length) / (length * (real_t)2.0)) * length * (real_t)2.0 - length) : (real_t)0.0;
+ }
}
}
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index f3e83b26b9..52447bc59b 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -504,7 +504,7 @@ void GDMono::_init_godot_api_hashes() {
}
void GDMono::_init_exception_policy() {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+ PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/unhandled_exception_policy", PROPERTY_HINT_ENUM,
vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 129bc6af0b..1a0b0e357d 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1279,6 +1279,23 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
if (!p_font_data->face_init) {
+ // Get style flags and name.
+ if (fd->face->family_name != nullptr) {
+ p_font_data->font_name = String::utf8((const char *)fd->face->family_name);
+ }
+ if (fd->face->style_name != nullptr) {
+ p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
+ }
+ p_font_data->style_flags = 0;
+ if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ p_font_data->style_flags |= FONT_BOLD;
+ }
+ if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ p_font_data->style_flags |= FONT_ITALIC;
+ }
+ if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
+ p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ }
// Get supported scripts from OpenType font data.
p_font_data->supported_scripts.clear();
unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
@@ -1648,6 +1665,66 @@ void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data
fd->data_size = p_data_size;
}
+void TextServerAdvanced::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerAdvanced::font_get_style(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerAdvanced::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerAdvanced::font_get_style_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerAdvanced::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerAdvanced::font_get_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2931,6 +3008,27 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped
return sd->direction;
}
+void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->custom_punct != p_punct) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->custom_punct = p_punct;
+ invalidate(sd);
+ }
+}
+
+String TextServerAdvanced::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, String());
+ return sd->custom_punct;
+}
+
void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -3230,6 +3328,7 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->orientation = sd->orientation;
new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
new_sd->line_breaks_valid = sd->line_breaks_valid;
new_sd->justification_ops_valid = sd->justification_ops_valid;
@@ -3810,6 +3909,9 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
const char32_t *ch = sd->text.ptr();
Glyph *sd_glyphs = sd->glyphs.ptrw();
+ int c_punct_size = sd->custom_punct.length();
+ const char32_t *c_punct = sd->custom_punct.ptr();
+
for (i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = ch[sd_glyphs[i].start - sd->start];
@@ -3822,12 +3924,21 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
}
+ if (c_punct_size == 0) {
+ if (u_ispunct(c) && c != 0x005F) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ } else {
+ for (int j = 0; j < c_punct_size; j++) {
+ if (c_punct[j] == c) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ break;
+ }
+ }
+ }
if (is_underscore(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
- if (u_ispunct(c) && c != 0x005F) {
- sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
- }
if (breaks.has(sd->glyphs[i].start)) {
if (breaks[sd->glyphs[i].start]) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 15f3a7f1a9..eb8316b200 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -177,6 +177,10 @@ class TextServerAdvanced : public TextServer {
Dictionary variation_coordinates;
float oversampling = 0.f;
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
+
Map<Vector2i, FontDataForSizeAdvanced *> cache;
bool face_init = false;
@@ -321,6 +325,15 @@ public:
virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override;
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_style_name(RID p_font_rid) const override;
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_name(RID p_font_rid) const override;
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
virtual bool font_is_antialiased(RID p_font_rid) const override;
@@ -450,6 +463,9 @@ public:
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index e0e77ff753..43a636e484 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -736,6 +736,23 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
if (!p_font_data->face_init) {
+ // Get style flags and name.
+ if (fd->face->family_name != nullptr) {
+ p_font_data->font_name = String::utf8((const char *)fd->face->family_name);
+ }
+ if (fd->face->style_name != nullptr) {
+ p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
+ }
+ p_font_data->style_flags = 0;
+ if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ p_font_data->style_flags |= FONT_BOLD;
+ }
+ if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ p_font_data->style_flags |= FONT_ITALIC;
+ }
+ if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
+ p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ }
// Read OpenType variations.
p_font_data->supported_varaitions.clear();
if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
@@ -826,6 +843,66 @@ void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data
fd->data_size = p_data_size;
}
+void TextServerFallback::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerFallback::font_get_style(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerFallback::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerFallback::font_get_style_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerFallback::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerFallback::font_get_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
@@ -2030,6 +2107,27 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped
return TextServer::DIRECTION_LTR;
}
+void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->custom_punct != p_punct) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->custom_punct = p_punct;
+ invalidate(sd);
+ }
+}
+
+String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, String());
+ return sd->custom_punct;
+}
+
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
@@ -2332,6 +2430,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->orientation = sd->orientation;
new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
new_sd->line_breaks_valid = sd->line_breaks_valid;
new_sd->justification_ops_valid = sd->justification_ops_valid;
@@ -2615,27 +2714,41 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
int sd_size = sd->glyphs.size();
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ int c_punct_size = sd->custom_punct.length();
+ const char32_t *c_punct = sd->custom_punct.ptr();
+
for (int i = 0; i < sd_size; i++) {
- if (sd->glyphs[i].count > 0) {
- char32_t c = sd->text[sd->glyphs[i].start];
- if (is_punct(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ if (sd_glyphs[i].count > 0) {
+ char32_t c = sd->text[sd_glyphs[i].start];
+ if (c_punct_size == 0) {
+ if (is_punct(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ } else {
+ for (int j = 0; j < c_punct_size; j++) {
+ if (c_punct[j] == c) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ break;
+ }
+ }
}
if (is_underscore(c)) {
sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
if (is_whitespace(c) && !is_linebreak(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
- sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
}
if (is_linebreak(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
}
if (c == 0x0009 || c == 0x000b) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB;
+ sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
}
- i += (sd->glyphs[i].count - 1);
+ i += (sd_glyphs[i].count - 1);
}
}
sd->line_breaks_valid = true;
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index fb7de8f443..2f495115e0 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -142,6 +142,10 @@ class TextServerFallback : public TextServer {
Dictionary variation_coordinates;
float oversampling = 0.f;
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
+
Map<Vector2i, FontDataForSizeFallback *> cache;
bool face_init = false;
@@ -234,6 +238,15 @@ public:
virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override;
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_style_name(RID p_font_rid) const override;
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_name(RID p_font_rid) const override;
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
virtual bool font_is_antialiased(RID p_font_rid) const override;
@@ -361,6 +374,9 @@ public:
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index 942d92311b..f4abb3c122 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -140,73 +140,76 @@
</constant>
<constant name="MATH_WRAPF" value="42" enum="BuiltinFunc">
</constant>
- <constant name="LOGIC_MAX" value="43" enum="BuiltinFunc">
+ <constant name="MATH_PINGPONG" value="43" enum="BuiltinFunc">
+ Return the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
+ </constant>
+ <constant name="LOGIC_MAX" value="44" enum="BuiltinFunc">
Return the greater of the two numbers, also known as their maximum.
</constant>
- <constant name="LOGIC_MIN" value="44" enum="BuiltinFunc">
+ <constant name="LOGIC_MIN" value="45" enum="BuiltinFunc">
Return the lesser of the two numbers, also known as their minimum.
</constant>
- <constant name="LOGIC_CLAMP" value="45" enum="BuiltinFunc">
+ <constant name="LOGIC_CLAMP" value="46" enum="BuiltinFunc">
Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code].
</constant>
- <constant name="LOGIC_NEAREST_PO2" value="46" enum="BuiltinFunc">
+ <constant name="LOGIC_NEAREST_PO2" value="47" enum="BuiltinFunc">
Return the nearest power of 2 to the input.
</constant>
- <constant name="OBJ_WEAKREF" value="47" enum="BuiltinFunc">
+ <constant name="OBJ_WEAKREF" value="48" enum="BuiltinFunc">
Create a [WeakRef] from the input.
</constant>
- <constant name="TYPE_CONVERT" value="48" enum="BuiltinFunc">
+ <constant name="TYPE_CONVERT" value="49" enum="BuiltinFunc">
Convert between types.
</constant>
- <constant name="TYPE_OF" value="49" enum="BuiltinFunc">
+ <constant name="TYPE_OF" value="50" enum="BuiltinFunc">
Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
</constant>
- <constant name="TYPE_EXISTS" value="50" enum="BuiltinFunc">
+ <constant name="TYPE_EXISTS" value="51" enum="BuiltinFunc">
Checks if a type is registered in the [ClassDB].
</constant>
- <constant name="TEXT_CHAR" value="51" enum="BuiltinFunc">
+ <constant name="TEXT_CHAR" value="52" enum="BuiltinFunc">
Return a character with the given ascii value.
</constant>
- <constant name="TEXT_STR" value="52" enum="BuiltinFunc">
+ <constant name="TEXT_STR" value="53" enum="BuiltinFunc">
Convert the input to a string.
</constant>
- <constant name="TEXT_PRINT" value="53" enum="BuiltinFunc">
+ <constant name="TEXT_PRINT" value="54" enum="BuiltinFunc">
Print the given string to the output window.
</constant>
- <constant name="TEXT_PRINTERR" value="54" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTERR" value="55" enum="BuiltinFunc">
Print the given string to the standard error output.
</constant>
- <constant name="TEXT_PRINTRAW" value="55" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTRAW" value="56" enum="BuiltinFunc">
Print the given string to the standard output, without adding a newline.
</constant>
- <constant name="TEXT_PRINT_VERBOSE" value="56" enum="BuiltinFunc">
+ <constant name="TEXT_PRINT_VERBOSE" value="57" enum="BuiltinFunc">
</constant>
- <constant name="VAR_TO_STR" value="57" enum="BuiltinFunc">
+ <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc">
Serialize a [Variant] to a string.
</constant>
- <constant name="STR_TO_VAR" value="58" enum="BuiltinFunc">
+ <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc">
Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR].
</constant>
- <constant name="VAR_TO_BYTES" value="59" enum="BuiltinFunc">
+ <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc">
Serialize a [Variant] to a [PackedByteArray].
</constant>
- <constant name="BYTES_TO_VAR" value="60" enum="BuiltinFunc">
+ <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc">
Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES].
</constant>
- <constant name="MATH_SMOOTHSTEP" value="61" enum="BuiltinFunc">
+ <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc">
Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
[codeblock]
var t = clamp((weight - from) / (to - from), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
[/codeblock]
</constant>
- <constant name="MATH_POSMOD" value="62" enum="BuiltinFunc">
+ <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc">
</constant>
- <constant name="MATH_LERP_ANGLE" value="63" enum="BuiltinFunc">
+ <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc">
</constant>
- <constant name="TEXT_ORD" value="64" enum="BuiltinFunc">
+ <constant name="TEXT_ORD" value="65" enum="BuiltinFunc">
</constant>
- <constant name="FUNC_MAX" value="65" enum="BuiltinFunc">
+ <constant name="FUNC_MAX" value="66" enum="BuiltinFunc">
Represents the size of the [enum BuiltinFunc] enum.
</constant>
</constants>
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 7e01031128..54a5e1449f 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -81,6 +81,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"db2linear",
"wrapi",
"wrapf",
+ "pingpong",
"max",
"min",
"clamp",
@@ -190,6 +191,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case MATH_FMOD:
case MATH_FPOSMOD:
case MATH_POSMOD:
+ case MATH_PINGPONG:
case MATH_POW:
case MATH_EASE:
case MATH_SNAPPED:
@@ -381,6 +383,13 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case MATH_DB2LINEAR: {
return PropertyInfo(Variant::FLOAT, "db");
} break;
+ case MATH_PINGPONG: {
+ if (p_idx == 0) {
+ return PropertyInfo(Variant::FLOAT, "value");
+ } else {
+ return PropertyInfo(Variant::FLOAT, "length");
+ }
+ } break;
case MATH_WRAP: {
if (p_idx == 0) {
return PropertyInfo(Variant::INT, "value");
@@ -537,6 +546,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_RAD2DEG:
case MATH_LINEAR2DB:
case MATH_WRAPF:
+ case MATH_PINGPONG:
case MATH_DB2LINEAR: {
t = Variant::FLOAT;
} break;
@@ -859,6 +869,11 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(0);
*r_return = Math::db2linear((double)*p_inputs[0]);
} break;
+ case VisualScriptBuiltinFunc::MATH_PINGPONG: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return = Math::pingpong((double)*p_inputs[0], (double)*p_inputs[1]);
+ } break;
case VisualScriptBuiltinFunc::MATH_WRAP: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
@@ -1206,6 +1221,7 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_DB2LINEAR);
BIND_ENUM_CONSTANT(MATH_WRAP);
BIND_ENUM_CONSTANT(MATH_WRAPF);
+ BIND_ENUM_CONSTANT(MATH_PINGPONG);
BIND_ENUM_CONSTANT(LOGIC_MAX);
BIND_ENUM_CONSTANT(LOGIC_MIN);
BIND_ENUM_CONSTANT(LOGIC_CLAMP);
@@ -1296,6 +1312,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/pingpong", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_PINGPONG>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/max", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/min", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index f9eb7e983f..30f1f0d417 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -81,6 +81,7 @@ public:
MATH_DB2LINEAR,
MATH_WRAP,
MATH_WRAPF,
+ MATH_PINGPONG,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 7b4fbc67fc..c74d6f9a4a 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -2587,18 +2587,21 @@ void VisualScriptEditor::reload_text() {
String VisualScriptEditor::get_name() {
String name;
- if (!script->is_built_in()) {
- name = script->get_path().get_file();
- if (is_unsaved()) {
- if (script->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = script->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in scripts before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (script->is_built_in()) {
+ const String &script_name = script->get_name();
+ if (script_name != "") {
+ // If the built-in script has a custom resource name defined,
+ // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
- } else if (script->get_name() != "") {
- name = script->get_name();
- } else {
- name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index 5bd5648b6e..4c45be5210 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1667,7 +1667,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/depth_buffer_bits", PROPERTY_HINT_ENUM, "16 bits,24 bits [default],32 bits"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0));
@@ -2209,9 +2209,10 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
command_line_strings.push_back("--xr_mode_regular");
}
- bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer");
- if (use_32_bit_framebuffer) {
- command_line_strings.push_back("--use_depth_32");
+ int depth_buffer_bits_index = p_preset->get("graphics/depth_buffer_bits");
+ if (depth_buffer_bits_index >= 0 && depth_buffer_bits_index <= 2) {
+ int depth_buffer_bits = 16 + depth_buffer_bits_index * 8;
+ command_line_strings.push_back(vformat("--use_depth=%d", depth_buffer_bits));
}
bool immersive = p_preset->get("screen/immersive_mode");
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index d39ab30cda..d872d5ed8a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -119,7 +119,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
private Button mWiFiSettingsButton;
private XRMode xrMode = XRMode.REGULAR;
- private boolean use_32_bits = false;
+ private int depth_buffer_bits = 24;
private boolean use_immersive = false;
private boolean use_debug_opengl = false;
private boolean mStatePaused;
@@ -266,7 +266,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
if (videoDriver.equals("vulkan")) {
mRenderView = new GodotVulkanRenderView(activity, this);
} else {
- mRenderView = new GodotGLRenderView(activity, this, xrMode, use_32_bits,
+ mRenderView = new GodotGLRenderView(activity, this, xrMode, depth_buffer_bits,
use_debug_opengl);
}
@@ -506,8 +506,12 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
xrMode = XRMode.REGULAR;
} else if (command_line[i].equals(XRMode.OVR.cmdLineArg)) {
xrMode = XRMode.OVR;
- } else if (command_line[i].equals("--use_depth_32")) {
- use_32_bits = true;
+ } else if (command_line[i].startsWith("--use_depth=")) {
+ try {
+ depth_buffer_bits = Integer.parseInt(command_line[i].split("=")[1]);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
} else if (command_line[i].equals("--debug_opengl")) {
use_debug_opengl = true;
} else if (command_line[i].equals("--use_immersive")) {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 32aad8dc4f..8b77302491 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -78,10 +78,10 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
private final GodotRenderer godotRenderer;
private PointerIcon pointerIcon;
- public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, boolean p_use_32_bits,
+ public GodotGLRenderView(Context context, Godot godot, XRMode xrMode, int p_depth_buffer_bits,
boolean p_use_debug_opengl) {
super(context);
- GLUtils.use_32 = p_use_32_bits;
+ GLUtils.depth_buffer_bits = p_depth_buffer_bits;
GLUtils.use_debug_opengl = p_use_debug_opengl;
this.godot = godot;
@@ -209,18 +209,16 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView
* below.
*/
- if (GLUtils.use_32) {
- setEGLConfigChooser(translucent
- ? new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
- new RegularConfigChooser(8, 8, 8, 8, 16, stencil))
- : new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil,
- new RegularConfigChooser(5, 6, 5, 0, 16, stencil)));
-
- } else {
- setEGLConfigChooser(translucent
- ? new RegularConfigChooser(8, 8, 8, 8, 16, stencil)
- : new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
+ RegularConfigChooser configChooser =
+ new RegularFallbackConfigChooser(8, 8, 8, 8, 16, stencil,
+ new RegularConfigChooser(5, 6, 5, 0, 16, stencil));
+ if (GLUtils.depth_buffer_bits >= 24) {
+ configChooser = new RegularFallbackConfigChooser(8, 8, 8, 8, 24, stencil, configChooser);
+ if (GLUtils.depth_buffer_bits >= 32) {
+ configChooser = new RegularFallbackConfigChooser(8, 8, 8, 8, 32, stencil, configChooser);
+ }
}
+ setEGLConfigChooser(configChooser);
break;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
index 95870acda1..a23d030d4c 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java
@@ -75,9 +75,8 @@ public class GodotLib {
/**
* Invoked on the render thread when the underlying Android surface is created or recreated.
* @param p_surface
- * @param p_32_bits
*/
- public static native void newcontext(Surface p_surface, boolean p_32_bits);
+ public static native void newcontext(Surface p_surface);
/**
* Forward {@link Activity#onBackPressed()} event from the main thread to the GL thread.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
index 878a119c5c..12e452fc99 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderer.java
@@ -70,7 +70,7 @@ class GodotRenderer implements GLSurfaceView.Renderer {
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- GodotLib.newcontext(null, GLUtils.use_32);
+ GodotLib.newcontext(null);
for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
plugin.onGLSurfaceCreated(gl, config);
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
index 19588f8465..0d581785ab 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/GLUtils.java
@@ -44,7 +44,7 @@ public class GLUtils {
public static final boolean DEBUG = false;
- public static boolean use_32 = false;
+ public static int depth_buffer_bits; // No need to reiterate the default here
public static boolean use_debug_opengl = false;
private static final String[] ATTRIBUTES_NAMES = new String[] {
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
index a35f6ec5a7..b13f9bfeab 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkRenderer.kt
@@ -58,7 +58,7 @@ internal class VkRenderer {
* Called when the surface is created and signals the beginning of rendering.
*/
fun onVkSurfaceCreated(surface: Surface) {
- GodotLib.newcontext(surface, false)
+ GodotLib.newcontext(surface)
for (plugin in pluginRegistry.getAllPlugins()) {
plugin.onVkSurfaceCreated(surface)
diff --git a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
index e690c5b695..63c5381994 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/xr/regular/RegularFallbackConfigChooser.java
@@ -38,7 +38,7 @@ import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
-/* Fallback if 32bit View is not supported*/
+/* Fallback if the requested configuration is not supported */
public class RegularFallbackConfigChooser extends RegularConfigChooser {
private static final String TAG = RegularFallbackConfigChooser.class.getSimpleName();
@@ -55,7 +55,6 @@ public class RegularFallbackConfigChooser extends RegularConfigChooser {
if (ec == null) {
Log.w(TAG, "Trying ConfigChooser fallback");
ec = fallback.chooseConfig(egl, display, configs);
- GLUtils.use_32 = false;
}
return ec;
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index d971727269..c7f19e4be2 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -182,11 +182,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
}
}
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits) {
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface) {
if (os_android) {
if (step.get() == 0) {
// During startup
- os_android->set_context_is_16_bits(!p_32_bits);
if (p_surface) {
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
os_android->set_native_window(native_window);
diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h
index 63e9e6d8e5..7ea74480cb 100644
--- a/platform/android/java_godot_lib_jni.h
+++ b/platform/android/java_godot_lib_jni.h
@@ -41,7 +41,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, jclass clazz, jobject p_surface, jint p_width, jint p_height);
-JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface, jboolean p_32_bits);
+JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *env, jclass clazz, jobject p_surface);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jclass clazz);
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jclass clazz);
void touch_preprocessing(JNIEnv *env, jclass clazz, jint input_device, jint ev, jint pointer, jint pointer_count, jfloatArray positions, jint buttons_mask = 0, jfloat vertical_factor = 0, jfloat horizontal_factor = 0);
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 1a4c5ec00f..0e5e10bc0a 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -261,13 +261,6 @@ Size2i OS_Android::get_display_size() const {
return display_size;
}
-void OS_Android::set_context_is_16_bits(bool p_is_16) {
-#if defined(GLES3_ENABLED)
- //if (rasterizer)
- // rasterizer->set_force_16_bits_fbo(p_is_16);
-#endif
-}
-
void OS_Android::set_opengl_extensions(const char *p_gl_extensions) {
#if defined(GLES3_ENABLED)
ERR_FAIL_COND(!p_gl_extensions);
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index d2e0e7d1e9..a62f79952c 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -102,7 +102,6 @@ public:
void set_display_size(const Size2i &p_size);
Size2i get_display_size() const;
- void set_context_is_16_bits(bool p_is_16);
void set_opengl_extensions(const char *p_gl_extensions);
void set_native_window(ANativeWindow *p_native_window);
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index c4e828bdb2..9349d378d0 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -3685,11 +3685,18 @@ void DisplayServerX11::process_events() {
} break;
case KeyPress:
case KeyRelease: {
+#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED
+ if (event.type == KeyPress) {
+ DEBUG_LOG_X11("[%u] KeyPress window=%lu (%u), keycode=%u, time=%lu \n", frame, event.xkey.window, window_id, event.xkey.keycode, event.xkey.time);
+ } else {
+ DEBUG_LOG_X11("[%u] KeyRelease window=%lu (%u), keycode=%u, time=%lu \n", frame, event.xkey.window, window_id, event.xkey.keycode, event.xkey.time);
+ }
+#endif
last_timestamp = event.xkey.time;
// key event is a little complex, so
// it will be handled in its own function.
- _handle_key_event(window_id, (XKeyEvent *)&event, events, event_index);
+ _handle_key_event(window_id, &event.xkey, events, event_index);
} break;
case SelectionNotify:
diff --git a/platform/windows/detect.py b/platform/windows/detect.py
index 22fbbeb74f..20deb35089 100644
--- a/platform/windows/detect.py
+++ b/platform/windows/detect.py
@@ -226,9 +226,10 @@ def configure_msvc(env, manual_msvc_config):
env.AppendUnique(CCFLAGS=["/MD"])
env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
- # Force to use Unicode encoding
- env.AppendUnique(CCFLAGS=["/utf-8"])
+ env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
+ env.AppendUnique(CCFLAGS=["/bigobj"]) # Allow big objects, no drawbacks.
env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++
+
if manual_msvc_config: # should be automatic if SCons found it
if os.getenv("WindowsSdkDir") is not None:
env.Prepend(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"])
@@ -421,6 +422,7 @@ def configure_mingw(env):
## Compile flags
env.Append(CCFLAGS=["-mwindows"])
+ env.Append(CCFLAGS=["-Wa,-mbig-obj"]) # Allow big objects, no drawbacks.
env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
env.Append(CPPDEFINES=[("WINVER", env["target_win_version"]), ("_WIN32_WINNT", env["target_win_version"])])
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index e2c9951692..aa97386399 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -42,6 +42,11 @@
#include "drivers/gles3/rasterizer_gles3.h"
#endif
+#if defined(__GNUC__)
+// Workaround GCC warning from -Wcast-function-type.
+#define GetProcAddress (void *)GetProcAddress
+#endif
+
static String format_error_message(DWORD id) {
LPWSTR messageBuffer = nullptr;
size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
diff --git a/platform/windows/gl_manager_windows.cpp b/platform/windows/gl_manager_windows.cpp
index 98205d6282..1ce8b0b040 100644
--- a/platform/windows/gl_manager_windows.cpp
+++ b/platform/windows/gl_manager_windows.cpp
@@ -197,9 +197,6 @@ Error GLManager_Windows::window_create(DisplayServer::WindowID p_window_id, HWND
return FAILED;
}
- // the display could be invalid .. check NYI
- GLDisplay &gl_display = _displays[win.gldisplay_id];
-
// make current
window_make_current(_windows.size() - 1);
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index a43d498a62..9d140f744f 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1101,6 +1101,10 @@ bool CharacterBody2D::move_and_slide() {
if (bs) {
Vector2 local_position = gt.elements[2] - bs->get_transform().elements[2];
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
+ } else {
+ // Body is removed or destroyed, invalidate floor.
+ current_platform_velocity = Vector2();
+ platform_rid = RID();
}
} else {
current_platform_velocity = Vector2();
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index d0506227b4..ea881b6d54 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -1145,6 +1145,10 @@ bool CharacterBody3D::move_and_slide() {
if (bs) {
Vector3 local_position = gt.origin - bs->get_transform().origin;
current_platform_velocity = bs->get_velocity_at_local_position(local_position);
+ } else {
+ // Body is removed or destroyed, invalidate floor.
+ current_platform_velocity = Vector3();
+ platform_rid = RID();
}
} else {
current_platform_velocity = Vector3();
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index b91a2869e4..e621f06ce9 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_space_2d.h"
+#include "animation_blend_tree.h"
#include "core/math/geometry_2d.h"
void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const {
@@ -531,6 +532,12 @@ double AnimationNodeBlendSpace2D::process(double p_time, bool p_seek) {
if (new_closest != closest && new_closest != -1) {
float from = 0.0;
if (blend_mode == BLEND_MODE_DISCRETE_CARRY && closest != -1) {
+ //for ping-pong loop
+ Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[closest].node);
+ Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
+ if (!na_c.is_null() && !na_n.is_null()) {
+ na_n->set_backward(na_c->is_backward());
+ }
//see how much animation remains
from = length_internal - blend_node(blend_points[closest].name, blend_points[closest].node, p_time, false, 0.0, FILTER_IGNORE, false);
}
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index d18e1a92d8..d6c5d0b51c 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -30,6 +30,7 @@
#include "animation_blend_tree.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
void AnimationNodeAnimation::set_animation(const StringName &p_name) {
@@ -83,30 +84,55 @@ double AnimationNodeAnimation::process(double p_time, bool p_seek) {
}
Ref<Animation> anim = ap->get_animation(animation);
-
- double step;
+ double anim_size = (double)anim->get_length();
+ double step = 0.0;
+ double prev_time = time;
+ int pingponged = 0;
+ bool current_backward = signbit(p_time);
if (p_seek) {
+ step = p_time - time;
time = p_time;
- step = 0;
} else {
- time = MAX(0, time + p_time);
- step = p_time;
+ p_time *= backward ? -1.0 : 1.0;
+ if (!(time == anim_size && !current_backward) && !(time == 0 && current_backward)) {
+ time = time + p_time;
+ step = p_time;
+ }
}
- double anim_size = anim->get_length();
-
- if (anim->has_loop()) {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_PINGPONG) {
if (anim_size) {
- time = Math::fposmod(time, anim_size);
+ if ((int)Math::floor(abs(time - prev_time) / anim_size) % 2 == 0) {
+ if (prev_time > 0 && time <= 0) {
+ backward = !backward;
+ pingponged = -1;
+ }
+ if (prev_time < anim_size && time >= anim_size) {
+ backward = !backward;
+ pingponged = 1;
+ }
+ }
+ time = Math::pingpong(time, anim_size);
}
-
- } else if (time > anim_size) {
- time = anim_size;
+ } else {
+ if (anim->get_loop_mode() == Animation::LoopMode::LOOP_LINEAR) {
+ if (anim_size) {
+ time = Math::fposmod(time, anim_size);
+ }
+ } else if (time < 0) {
+ time = 0;
+ } else if (time > anim_size) {
+ time = anim_size;
+ }
+ backward = false;
}
- blend_animation(animation, time, step, p_seek, 1.0);
-
+ if (play_mode == PLAY_MODE_FORWARD) {
+ blend_animation(animation, time, step, p_seek, 1.0, pingponged);
+ } else {
+ blend_animation(animation, anim_size - time, -step, p_seek, 1.0, pingponged);
+ }
set_parameter(this->time, time);
return anim_size - time;
@@ -116,11 +142,34 @@ String AnimationNodeAnimation::get_caption() const {
return "Animation";
}
+void AnimationNodeAnimation::set_play_mode(PlayMode p_play_mode) {
+ play_mode = p_play_mode;
+}
+
+AnimationNodeAnimation::PlayMode AnimationNodeAnimation::get_play_mode() const {
+ return play_mode;
+}
+
+void AnimationNodeAnimation::set_backward(bool p_backward) {
+ backward = p_backward;
+}
+
+bool AnimationNodeAnimation::is_backward() const {
+ return backward;
+}
+
void AnimationNodeAnimation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation", "name"), &AnimationNodeAnimation::set_animation);
ClassDB::bind_method(D_METHOD("get_animation"), &AnimationNodeAnimation::get_animation);
+ ClassDB::bind_method(D_METHOD("set_play_mode", "mode"), &AnimationNodeAnimation::set_play_mode);
+ ClassDB::bind_method(D_METHOD("get_play_mode"), &AnimationNodeAnimation::get_play_mode);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "play_mode", PROPERTY_HINT_ENUM, "Forward,Backward"), "set_play_mode", "get_play_mode");
+
+ BIND_ENUM_CONSTANT(PLAY_MODE_FORWARD);
+ BIND_ENUM_CONSTANT(PLAY_MODE_BACKWARD);
}
AnimationNodeAnimation::AnimationNodeAnimation() {
@@ -533,7 +582,7 @@ AnimationNodeBlend3::AnimationNodeBlend3() {
/////////////////////////////////
void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const {
- r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"));
+ r_list->push_back(PropertyInfo(Variant::FLOAT, scale, PROPERTY_HINT_RANGE, "-32,32,0.01,or_lesser,or_greater"));
}
Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const {
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index 258443a999..e55dfb58ed 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -42,12 +42,12 @@ class AnimationNodeAnimation : public AnimationRootNode {
uint64_t last_version = 0;
bool skip = false;
-protected:
- void _validate_property(PropertyInfo &property) const override;
-
- static void _bind_methods();
-
public:
+ enum PlayMode {
+ PLAY_MODE_FORWARD,
+ PLAY_MODE_BACKWARD
+ };
+
void get_parameter_list(List<PropertyInfo> *r_list) const override;
static Vector<String> (*get_editable_animation_list)();
@@ -58,9 +58,25 @@ public:
void set_animation(const StringName &p_name);
StringName get_animation() const;
+ void set_play_mode(PlayMode p_play_mode);
+ PlayMode get_play_mode() const;
+
+ void set_backward(bool p_backward);
+ bool is_backward() const;
+
AnimationNodeAnimation();
+
+protected:
+ void _validate_property(PropertyInfo &property) const override;
+ static void _bind_methods();
+
+private:
+ PlayMode play_mode = PLAY_MODE_FORWARD;
+ bool backward = false;
};
+VARIANT_ENUM_CAST(AnimationNodeAnimation::PlayMode)
+
class AnimationNodeOneShot : public AnimationNode {
GDCLASS(AnimationNodeOneShot, AnimationNode);
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index f91422ac1a..93339711bd 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -398,12 +398,13 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
}
}
-void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started) {
+void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current, bool p_seeked, bool p_started, int p_pingponged) {
_ensure_node_caches(p_anim);
ERR_FAIL_COND(p_anim->node_cache.size() != p_anim->animation->get_track_count());
Animation *a = p_anim->animation.operator->();
bool can_call = is_inside_tree() && !Engine::get_singleton()->is_editor_hint();
+ bool backward = signbit(p_delta);
for (int i = 0; i < a->get_track_count(); i++) {
// If an animation changes this animation (or it animates itself)
@@ -557,8 +558,8 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
continue; //eeh not worth it
}
- float first_key_time = a->track_get_key_time(i, 0);
- float transition = 1.0;
+ double first_key_time = a->track_get_key_time(i, 0);
+ double transition = 1.0;
int first_key = 0;
if (first_key_time == 0.0) {
@@ -566,13 +567,13 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
if (key_count == 1) {
continue; //with one key we can't do anything
}
- transition = a->track_get_key_transition(i, 0);
+ transition = (double)a->track_get_key_transition(i, 0);
first_key_time = a->track_get_key_time(i, 1);
first_key = 1;
}
if (p_time < first_key_time) {
- float c = Math::ease(p_time / first_key_time, transition);
+ double c = Math::ease(p_time / first_key_time, transition);
Variant first_value = a->track_get_key_value(i, first_key);
Variant interp_value;
Variant::interpolate(pa->capture, first_value, c, interp_value);
@@ -614,7 +615,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else if (p_is_current && p_delta != 0) {
List<int> indices;
- a->value_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->value_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -673,7 +674,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
List<int> indices;
- a->method_track_get_key_indices(i, p_time, p_delta, &indices);
+ a->method_track_get_key_indices(i, p_time, p_delta, &indices, p_pingponged);
for (int &E : indices) {
StringName method = a->method_track_get_name(i, E);
@@ -728,7 +729,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
TrackNodeCache::BezierAnim *ba = &E->get();
- float bezier = a->bezier_track_interpolate(i, p_time);
+ real_t bezier = a->bezier_track_interpolate(i, p_time);
if (ba->accum_pass != accum_pass) {
ERR_CONTINUE(cache_update_bezier_size >= NODE_CACHE_UPDATE_MAX);
cache_update_bezier[cache_update_bezier_size++] = ba;
@@ -789,7 +790,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -817,12 +818,14 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
nc->audio_start = p_time;
}
} else if (nc->audio_playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && p_time < nc->audio_start) {
- stop = true;
+ if (!loop) {
+ if ((p_time < nc->audio_start && !backward) || (p_time > nc->audio_start && backward)) {
+ stop = true;
+ }
} else if (nc->audio_len > 0) {
float len = nc->audio_start > p_time ? (a->get_length() - nc->audio_start) + p_time : p_time - nc->audio_start;
@@ -863,12 +866,23 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
Ref<Animation> anim = player->get_animation(anim_name);
- double at_anim_pos;
+ double at_anim_pos = 0.0;
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MIN((double)anim->get_length(), p_time - pos); //seek to end
+ } break;
+
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(p_time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(p_time - pos, (double)anim->get_length());
+ } break;
+
+ default:
+ break;
}
if (player->is_playing() || p_seeked) {
@@ -883,7 +897,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData *p_anim, double
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play);
+ a->track_get_key_indices_in_range(i, p_time, p_delta, &to_play, p_pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -913,46 +927,73 @@ void AnimationPlayer::_animation_process_data(PlaybackData &cd, double p_delta,
double next_pos = cd.pos + delta;
real_t len = cd.from->animation->get_length();
- bool loop = cd.from->animation->has_loop();
+ int pingponged = 0;
+
+ switch (cd.from->animation->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ if (next_pos < 0) {
+ next_pos = 0;
+ } else if (next_pos > len) {
+ next_pos = len;
+ }
- if (!loop) {
- if (next_pos < 0) {
- next_pos = 0;
- } else if (next_pos > len) {
- next_pos = len;
- }
+ bool backwards = signbit(delta); // Negative zero means playing backwards too
+ delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
- bool backwards = signbit(delta); // Negative zero means playing backwards too
- delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here)
+ if (&cd == &playback.current) {
+ if (!backwards && cd.pos <= len && next_pos == len) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos < len; // Notify only if not already at the end
+ }
- if (&cd == &playback.current) {
- if (!backwards && cd.pos <= len && next_pos == len) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos < len; // Notify only if not already at the end
+ if (backwards && cd.pos >= 0 && next_pos == 0) {
+ //playback finished
+ end_reached = true;
+ end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ }
}
+ } break;
- if (backwards && cd.pos >= 0 && next_pos == 0) {
- //playback finished
- end_reached = true;
- end_notify = cd.pos > 0; // Notify only if not already at the beginning
+ case Animation::LoopMode::LOOP_LINEAR: {
+ double looped_next_pos = Math::fposmod(next_pos, (double)len);
+ if (looped_next_pos == 0 && next_pos != 0) {
+ // Loop multiples of the length to it, rather than 0
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
}
- }
+ } break;
- } else {
- double looped_next_pos = Math::fposmod(next_pos, (double)len);
- if (looped_next_pos == 0 && next_pos != 0) {
- // Loop multiples of the length to it, rather than 0
- // so state at time=length is previewable in the editor
- next_pos = len;
- } else {
- next_pos = looped_next_pos;
- }
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ if ((int)Math::floor(abs(next_pos - cd.pos) / len) % 2 == 0) {
+ if (next_pos < 0 && cd.pos >= 0) {
+ cd.speed_scale *= -1.0;
+ pingponged = -1;
+ }
+ if (next_pos > len && cd.pos <= len) {
+ cd.speed_scale *= -1.0;
+ pingponged = 1;
+ }
+ }
+ double looped_next_pos = Math::pingpong(next_pos, (double)len);
+ if (looped_next_pos == 0 && next_pos != 0) {
+ // Loop multiples of the length to it, rather than 0
+ // so state at time=length is previewable in the editor
+ next_pos = len;
+ } else {
+ next_pos = looped_next_pos;
+ }
+ } break;
+
+ default:
+ break;
}
cd.pos = next_pos;
- _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started);
+ _animation_process_animation(cd.from, cd.pos, delta, p_blend, &cd == &playback.current, p_seeked, p_started, pingponged);
}
void AnimationPlayer::_animation_process2(double p_delta, bool p_started) {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index d9d88b5510..ea04918988 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -231,7 +231,7 @@ private:
NodePath root;
- void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false);
+ void _animation_process_animation(AnimationData *p_anim, double p_time, double p_delta, float p_interp, bool p_is_current = true, bool p_seeked = false, bool p_started = false, int p_pingponged = 0);
void _ensure_node_caches(AnimationData *p_anim, Node *p_root_override = nullptr);
void _animation_process_data(PlaybackData &cd, double p_delta, float p_blend, bool p_seeked, bool p_started);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 4b71fe7a01..87ae870364 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -32,6 +32,7 @@
#include "animation_blend_tree.h"
#include "core/config/engine.h"
+#include "scene/resources/animation.h"
#include "scene/scene_string_names.h"
#include "servers/audio/audio_stream.h"
@@ -87,7 +88,7 @@ void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) {
}
}
-void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend) {
+void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged) {
ERR_FAIL_COND(!state);
ERR_FAIL_COND(!state->player->has_animation(p_animation));
@@ -113,6 +114,7 @@ void AnimationNode::blend_animation(const StringName &p_animation, real_t p_time
anim_state.time = p_time;
anim_state.animation = animation;
anim_state.seeked = p_seeked;
+ anim_state.pingponged = p_pingponged;
state->animation_states.push_back(anim_state);
}
@@ -418,7 +420,7 @@ void AnimationNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters);
ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters);
- ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation);
+ ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend", "pingponged"), &AnimationNode::blend_animation, DEFVAL(0));
ClassDB::bind_method(D_METHOD("blend_node", "name", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true));
ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true));
@@ -898,6 +900,8 @@ void AnimationTree::_process_graph(real_t p_delta) {
double delta = as.delta;
real_t weight = as.blend;
bool seeked = as.seeked;
+ int pingponged = as.pingponged;
+ bool backward = signbit(delta);
for (int i = 0; i < a->get_track_count(); i++) {
NodePath path = a->track_get_path(i);
@@ -939,27 +943,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 loc[2];
- if (prev_time > time) {
- Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, (double)a->get_length(), &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->position_track_interpolate(i, 0, &loc[1]);
+ t->loc += (loc[1] - loc[0]) * blend;
+ prev_time = 0;
}
-
- a->position_track_interpolate(i, a->get_length(), &loc[1]);
-
- t->loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
}
Error err = a->position_track_interpolate(i, prev_time, &loc[0]);
@@ -968,17 +1008,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->position_track_interpolate(i, time, &loc[1]);
-
t->loc += (loc[1] - loc[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 loc;
Error err = a->position_track_interpolate(i, time, &loc);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1000,29 +1036,65 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Quaternion rot[2];
- if (prev_time > time) {
- Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, (double)a->get_length(), &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->rotation_track_interpolate(i, 0, &rot[1]);
+ Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
+ t->rot = (t->rot * q).normalized();
+ prev_time = 0;
}
-
- a->rotation_track_interpolate(i, a->get_length(), &rot[1]);
-
- Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
- t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
}
Error err = a->rotation_track_interpolate(i, prev_time, &rot[0]);
@@ -1031,18 +1103,14 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->rotation_track_interpolate(i, time, &rot[1]);
-
Quaternion q = Quaternion().slerp(rot[0].normalized().inverse() * rot[1].normalized(), blend).normalized();
t->rot = (t->rot * q).normalized();
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Quaternion rot;
Error err = a->rotation_track_interpolate(i, time, &rot);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1071,28 +1139,63 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
if (track->root_motion) {
- real_t prev_time = time - delta;
- if (prev_time < 0) {
- if (!a->has_loop()) {
- prev_time = 0;
- } else {
- prev_time = a->get_length() + prev_time;
+ double prev_time = time - delta;
+ if (!backward) {
+ if (prev_time < 0) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = 0;
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
+ }
+ } else {
+ if (prev_time > a->get_length()) {
+ switch (a->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ prev_time = (double)a->get_length();
+ } break;
+ case Animation::LOOP_LINEAR: {
+ prev_time = Math::fposmod(prev_time, (double)a->get_length());
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ prev_time = Math::pingpong(prev_time, (double)a->get_length());
+ } break;
+ default:
+ break;
+ }
}
}
Vector3 scale[2];
- if (prev_time > time) {
- Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
- if (err != OK) {
- continue;
+ if (!backward) {
+ if (prev_time > time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, (double)a->get_length(), &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
+ }
+ } else {
+ if (prev_time < time) {
+ Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
+ if (err != OK) {
+ continue;
+ }
+ a->scale_track_interpolate(i, 0, &scale[1]);
+ t->scale += (scale[1] - scale[0]) * blend;
+ prev_time = 0;
}
-
- a->scale_track_interpolate(i, a->get_length(), &scale[1]);
-
- t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
}
Error err = a->scale_track_interpolate(i, prev_time, &scale[0]);
@@ -1101,17 +1204,13 @@ void AnimationTree::_process_graph(real_t p_delta) {
}
a->scale_track_interpolate(i, time, &scale[1]);
-
t->scale += (scale[1] - scale[0]) * blend;
-
- prev_time = 0;
+ prev_time = !backward ? 0 : (double)a->get_length();
} else {
Vector3 scale;
Error err = a->scale_track_interpolate(i, time, &scale);
- //ERR_CONTINUE(err!=OK); //used for testing, should be removed
-
if (err != OK) {
continue;
}
@@ -1164,7 +1263,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
List<int> indices;
- a->value_track_get_key_indices(i, time, delta, &indices);
+ a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
Variant value = a->track_get_key_value(i, F);
@@ -1181,7 +1280,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
List<int> indices;
- a->method_track_get_key_indices(i, time, delta, &indices);
+ a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
for (int &F : indices) {
StringName method = a->method_track_get_name(i, F);
@@ -1264,7 +1363,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
@@ -1292,12 +1391,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
t->start = time;
}
} else if (t->playing) {
- bool loop = a->has_loop();
+ bool loop = a->get_loop_mode() != Animation::LoopMode::LOOP_NONE;
bool stop = false;
- if (!loop && time < t->start) {
- stop = true;
+ if (!loop) {
+ if (delta > 0) {
+ if (time < t->start) {
+ stop = true;
+ }
+ } else if (delta < 0) {
+ if (time > t->start) {
+ stop = true;
+ }
+ }
} else if (t->len > 0) {
real_t len = t->start > time ? (a->get_length() - t->start) + time : time - t->start;
@@ -1331,7 +1438,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
continue;
}
- if (delta == 0 || seeked) {
+ if (seeked) {
//seek
int idx = a->track_find_key(i, time);
if (idx < 0) {
@@ -1347,12 +1454,20 @@ void AnimationTree::_process_graph(real_t p_delta) {
Ref<Animation> anim = player2->get_animation(anim_name);
- real_t at_anim_pos;
-
- if (anim->has_loop()) {
- at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
- } else {
- at_anim_pos = MAX(anim->get_length(), time - pos); //seek to end
+ real_t at_anim_pos = 0.0;
+
+ switch (anim->get_loop_mode()) {
+ case Animation::LoopMode::LOOP_NONE: {
+ at_anim_pos = MAX((double)anim->get_length(), time - pos); //seek to end
+ } break;
+ case Animation::LoopMode::LOOP_LINEAR: {
+ at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); //seek to loop
+ } break;
+ case Animation::LoopMode::LOOP_PINGPONG: {
+ at_anim_pos = Math::pingpong(time - pos, (double)a->get_length());
+ } break;
+ default:
+ break;
}
if (player2->is_playing() || seeked) {
@@ -1367,7 +1482,7 @@ void AnimationTree::_process_graph(real_t p_delta) {
} else {
//find stuff to play
List<int> to_play;
- a->track_get_key_indices_in_range(i, time, delta, &to_play);
+ a->track_get_key_indices_in_range(i, time, delta, &to_play, pingponged);
if (to_play.size()) {
int idx = to_play.back()->get();
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 5abea39d20..6fc051fa41 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -66,6 +66,7 @@ public:
const Vector<real_t> *track_blends = nullptr;
real_t blend = 0.0;
bool seeked = false;
+ int pingponged = 0;
};
struct State {
@@ -98,9 +99,10 @@ public:
real_t _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, real_t *r_max = nullptr);
protected:
- void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend);
+ void blend_animation(const StringName &p_animation, real_t p_time, real_t p_delta, bool p_seeked, real_t p_blend, int p_pingponged = 0);
real_t blend_node(const StringName &p_sub_path, Ref<AnimationNode> p_node, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
real_t blend_input(int p_input, real_t p_time, bool p_seek, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
+
void make_invalid(const String &p_reason);
static void _bind_methods();
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index b8cb618171..50908f6a77 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -243,11 +243,9 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
if (p_gl.font_rid != RID()) {
if (p_font_shadow_color.a > 0) {
TS->font_draw_glyph(p_gl.font_rid, p_canvas, p_gl.font_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
- if (p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + Vector2(-shadow_ofs.x, -shadow_ofs.y), p_gl.index, p_font_shadow_color);
- }
+ }
+ if (p_font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_shadow_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off) + shadow_ofs, p_gl.index, p_font_shadow_color);
}
if (p_font_outline_color.a != 0.0 && p_outline_size > 0) {
TS->font_draw_glyph_outline(p_gl.font_rid, p_canvas, p_gl.font_size, p_outline_size, p_ofs + Vector2(p_gl.x_off, p_gl.y_off), p_gl.index, p_font_outline_color);
@@ -397,7 +395,7 @@ void Label::_notification(int p_what) {
int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
// Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
- if (font_shadow_color.a > 0 || (font_outline_color.a != 0.0 && outline_size > 0)) {
+ if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
Vector2 offset = ofs;
// Draw RTL ellipsis string when necessary.
if (rtl && ellipsis_pos >= 0) {
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 0b85b4d3d2..15fd22ced5 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -664,7 +664,9 @@ void LineEdit::_notification(int p_what) {
}
Ref<Font> font = get_theme_font(SNAME("font"));
- style->draw(ci, Rect2(Point2(), size));
+ if (!flat) {
+ style->draw(ci, Rect2(Point2(), size));
+ }
if (has_focus()) {
get_theme_stylebox(SNAME("focus"))->draw(ci, Rect2(Point2(), size));
@@ -1966,6 +1968,17 @@ Ref<Texture2D> LineEdit::get_right_icon() {
return right_icon;
}
+void LineEdit::set_flat(bool p_enabled) {
+ if (flat != p_enabled) {
+ flat = p_enabled;
+ update();
+ }
+}
+
+bool LineEdit::is_flat() const {
+ return flat;
+}
+
void LineEdit::_text_changed() {
_emit_text_change();
_clear_redo();
@@ -2214,6 +2227,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_deselect_on_focus_loss_enabled"), &LineEdit::is_deselect_on_focus_loss_enabled);
ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon);
ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon);
+ ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat);
+ ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat);
ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text")));
ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
@@ -2269,6 +2284,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 3364e02e01..2ec7f08865 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -130,6 +130,7 @@ private:
bool middle_mouse_paste_enabled = true;
Ref<Texture2D> right_icon;
+ bool flat = false;
struct Selection {
int begin = 0;
@@ -332,6 +333,9 @@ public:
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> get_right_icon();
+ void set_flat(bool p_enabled);
+ bool is_flat() const;
+
virtual bool is_text_field() const override;
void show_virtual_keyboard();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f1efbbda98..308880ef18 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -621,7 +621,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
}
-int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &p_shadow_ofs) {
+int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs) {
Vector2 off;
ERR_FAIL_COND_V(p_frame == nullptr, 0);
@@ -804,7 +804,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
for (int j = 0; j < frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_as_outline, p_shadow_ofs);
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs);
}
idx++;
}
@@ -824,7 +824,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
int size = _find_outline_size(it, p_outline_size);
Color font_color = _find_outline_color(it, p_outline_color);
- if (size <= 0) {
+ Color font_shadow_color = p_font_shadow_color;
+ if ((size <= 0 || font_color.a == 0) && (font_shadow_color.a == 0)) {
gloff.x += glyphs[i].advance;
continue;
}
@@ -871,9 +872,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
font_color.a = faded_visibility;
+ font_shadow_color.a = faded_visibility;
}
- bool visible = (font_color.a != 0);
+ bool visible = (font_color.a != 0) || (font_shadow_color.a != 0);
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
@@ -942,18 +944,19 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
}
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
-
// Draw glyph outlines.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
if (frid != RID()) {
- if (p_shadow_as_outline) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff + Vector2(-shadow_ofs.x, -shadow_ofs.y), gl, p_font_shadow_color);
+ if (font_shadow_color.a > 0) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ }
+ if (font_color.a != 0.0 && size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
}
gloff.x += glyphs[i].advance;
@@ -1470,7 +1473,7 @@ void RichTextLabel::_notification(int p_what) {
Color outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- bool use_outline = get_theme_constant(SNAME("shadow_as_outline"));
+ int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
visible_paragraph_count = 0;
@@ -1480,7 +1483,7 @@ void RichTextLabel::_notification(int p_what) {
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
- visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, use_outline, shadow_ofs);
+ visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs);
ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
from_line++;
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index f3c4c11cc8..5b58f14d96 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -412,7 +412,7 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
- int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, bool p_shadow_as_outline, const Point2 &shadow_ofs);
+ int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
String _roman(int p_num, bool p_capitalize) const;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 71d2774335..3700e87839 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -449,8 +449,8 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
return true;
} else if (name == "length") {
r_ret = length;
- } else if (name == "loop") {
- r_ret = loop;
+ } else if (name == "loop_mode") {
+ r_ret = loop_mode;
} else if (name == "step") {
r_ret = step;
} else if (name.begins_with("tracks/")) {
@@ -2231,7 +2231,7 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
}
template <class K>
-int Animation::_find(const Vector<K> &p_keys, double p_time) const {
+int Animation::_find(const Vector<K> &p_keys, double p_time, bool p_backward) const {
int len = p_keys.size();
if (len == 0) {
return -2;
@@ -2261,8 +2261,14 @@ int Animation::_find(const Vector<K> &p_keys, double p_time) const {
}
}
- if (keys[middle].time > p_time) {
- middle--;
+ if (!p_backward) {
+ if (keys[middle].time > p_time) {
+ middle--;
+ }
+ } else {
+ if (keys[middle].time < p_time) {
+ middle++;
+ }
}
return middle;
@@ -2385,7 +2391,7 @@ real_t Animation::_cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, c
}
template <class T>
-T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const {
+T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward) const {
int len = _find(p_keys, length) + 1; // try to find last key (there may be more past the end)
if (len <= 0) {
@@ -2403,7 +2409,7 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
return p_keys[0].value;
}
- int idx = _find(p_keys, p_time);
+ int idx = _find(p_keys, p_time, p_backward);
ERR_FAIL_COND_V(idx == -2, T());
@@ -2412,24 +2418,42 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
real_t c = 0.0;
// prepare for all cases of interpolation
- if (loop && p_loop_wrap) {
+ if ((loop_mode == LOOP_LINEAR || loop_mode == LOOP_PINGPONG) && p_loop_wrap) {
// loop
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ // no backward
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
- }
+ next = 0;
+ real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
+ real_t from = p_time - p_keys[idx].time;
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
+ }
} else {
+ // on loop, behind first key
+ idx = len - 1;
next = 0;
- real_t delta = (length - p_keys[idx].time) + p_keys[next].time;
- real_t from = p_time - p_keys[idx].time;
+ real_t endtime = (length - p_keys[idx].time);
+ if (endtime < 0) { // may be keys past the end
+ endtime = 0;
+ }
+ real_t delta = endtime + p_keys[next].time;
+ real_t from = endtime + p_time;
if (Math::is_zero_approx(delta)) {
c = 0;
@@ -2437,49 +2461,81 @@ T Animation::_interpolate(const Vector<TKey<T>> &p_keys, double p_time, Interpol
c = from / delta;
}
}
-
} else {
- // on loop, behind first key
- idx = len - 1;
- next = 0;
- real_t endtime = (length - p_keys[idx].time);
- if (endtime < 0) { // may be keys past the end
- endtime = 0;
- }
- real_t delta = endtime + p_keys[next].time;
- real_t from = endtime + p_time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ // backward
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ } else {
+ next = len - 1;
+ real_t delta = p_keys[idx].time + (length - p_keys[next].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ // on loop, in front of last key
+ idx = 0;
+ next = len - 1;
+ real_t endtime = p_keys[idx].time;
+ if (endtime > length) // may be keys past the end
+ endtime = length;
+ real_t delta = p_keys[next].time - endtime;
+ real_t from = p_time - endtime;
+
+ if (Math::is_zero_approx(delta))
+ c = 0;
+ else
+ c = from / delta;
}
}
-
} else { // no loop
-
- if (idx >= 0) {
- if ((idx + 1) < len) {
- next = idx + 1;
- real_t delta = p_keys[next].time - p_keys[idx].time;
- real_t from = p_time - p_keys[idx].time;
-
- if (Math::is_zero_approx(delta)) {
- c = 0;
+ if (!p_backward) {
+ if (idx >= 0) {
+ if (idx < len - 1) {
+ next = idx + 1;
+ real_t delta = p_keys[next].time - p_keys[idx].time;
+ real_t from = p_time - p_keys[idx].time;
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
} else {
- c = from / delta;
+ next = idx;
}
-
} else {
- next = idx;
+ idx = next = 0;
}
-
} else {
- // only allow extending first key to anim start if looping
- if (loop) {
- idx = next = 0;
+ if (idx <= len - 1) {
+ if (idx > 0) {
+ next = idx - 1;
+ real_t delta = (length - p_keys[next].time) - (length - p_keys[idx].time);
+ real_t from = (length - p_time) - (length - p_keys[idx].time);
+
+ if (Math::is_zero_approx(delta)) {
+ c = 0;
+ } else {
+ c = from / delta;
+ }
+
+ } else {
+ next = idx;
+ }
} else {
- result = false;
+ idx = next = len - 1;
}
}
}
@@ -2583,7 +2639,7 @@ void Animation::_value_track_get_key_indices_in_range(const ValueTrack *vt, doub
}
}
-void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_VALUE);
@@ -2597,30 +2653,50 @@ void Animation::value_track_get_key_indices(int p_track, double p_time, double p
SWAP(from_time, to_time);
}
- if (loop) {
- from_time = Math::fposmod(from_time, length);
- to_time = Math::fposmod(to_time, length);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
- _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ from_time = Math::fposmod(from_time, length);
+ to_time = Math::fposmod(to_time, length);
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ from_time = Math::pingpong(from_time, length);
+ to_time = Math::pingpong(to_time, length);
+
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, 0, from_time, p_indices);
+ _value_track_get_key_indices_in_range(vt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ _value_track_get_key_indices_in_range(vt, from_time, length, p_indices);
+ _value_track_get_key_indices_in_range(vt, to_time, length, p_indices);
+ return;
+ }
+ } break;
}
_value_track_get_key_indices_in_range(vt, from_time, to_time, p_indices);
@@ -2679,7 +2755,7 @@ void Animation::_track_get_key_indices_in_range(const Vector<T> &p_array, double
}
}
-void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
const Track *t = tracks[p_track];
@@ -2690,114 +2766,255 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
-
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
-
- if (from_time > to_time) {
- // handle loop by splitting
-
- switch (t->type) {
- case TYPE_POSITION_3D: {
- const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- if (tt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
-
- } else {
- _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
- _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
- }
-
- } break;
- case TYPE_ROTATION_3D: {
- const RotationTrack *rt = static_cast<const RotationTrack *>(t);
- if (rt->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- } else {
- _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
- _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- } break;
- case TYPE_SCALE_3D: {
- const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
- if (st->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ if (from_time > to_time) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
+ }
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
+ }
- } else {
- _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
- _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ if ((int)Math::floor(abs(p_delta) / length) % 2 == 0) {
+ if (p_pingponged == -1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(tt->positions, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(st->scales, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, from_time, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, 0, from_time, p_indices);
+ _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
+ } break;
}
-
- } break;
- case TYPE_BLEND_SHAPE: {
- const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
- if (bst->compressed_track >= 0) {
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
- _get_compressed_key_indices_in_range<1>(bst->compressed_track, 0, to_time, p_indices);
-
- } else {
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
- _track_get_key_indices_in_range(bst->blend_shapes, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ // handle loop by splitting
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ const PositionTrack *tt = static_cast<const PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(tt->positions, from_time, length, p_indices);
+ _track_get_key_indices_in_range(tt->positions, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ const RotationTrack *rt = static_cast<const RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(rt->rotations, from_time, length, p_indices);
+ _track_get_key_indices_in_range(rt->rotations, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(st->scales, from_time, length, p_indices);
+ _track_get_key_indices_in_range(st->scales, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, length, p_indices);
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, to_time, length, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bst->blend_shapes, to_time, length, p_indices);
+ }
+ } break;
+ case TYPE_VALUE: {
+ const ValueTrack *vt = static_cast<const ValueTrack *>(t);
+ _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(vt->values, to_time, length, p_indices);
+ } break;
+ case TYPE_METHOD: {
+ const MethodTrack *mt = static_cast<const MethodTrack *>(t);
+ _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
+ _track_get_key_indices_in_range(mt->methods, to_time, length, p_indices);
+ } break;
+ case TYPE_BEZIER: {
+ const BezierTrack *bz = static_cast<const BezierTrack *>(t);
+ _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(bz->values, to_time, length, p_indices);
+ } break;
+ case TYPE_AUDIO: {
+ const AudioTrack *ad = static_cast<const AudioTrack *>(t);
+ _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(ad->values, to_time, length, p_indices);
+ } break;
+ case TYPE_ANIMATION: {
+ const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
+ _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
+ _track_get_key_indices_in_range(an->values, to_time, length, p_indices);
+ } break;
}
-
- } break;
- case TYPE_VALUE: {
- const ValueTrack *vt = static_cast<const ValueTrack *>(t);
- _track_get_key_indices_in_range(vt->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(vt->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_METHOD: {
- const MethodTrack *mt = static_cast<const MethodTrack *>(t);
- _track_get_key_indices_in_range(mt->methods, from_time, length, p_indices);
- _track_get_key_indices_in_range(mt->methods, 0, to_time, p_indices);
-
- } break;
- case TYPE_BEZIER: {
- const BezierTrack *bz = static_cast<const BezierTrack *>(t);
- _track_get_key_indices_in_range(bz->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(bz->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_AUDIO: {
- const AudioTrack *ad = static_cast<const AudioTrack *>(t);
- _track_get_key_indices_in_range(ad->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(ad->values, 0, to_time, p_indices);
-
- } break;
- case TYPE_ANIMATION: {
- const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
- _track_get_key_indices_in_range(an->values, from_time, length, p_indices);
- _track_get_key_indices_in_range(an->values, 0, to_time, p_indices);
-
- } break;
+ return;
+ }
}
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
-
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ } break;
}
switch (t->type) {
@@ -2808,7 +3025,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
}
-
} break;
case TYPE_ROTATION_3D: {
const RotationTrack *rt = static_cast<const RotationTrack *>(t);
@@ -2817,7 +3033,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
}
-
} break;
case TYPE_SCALE_3D: {
const ScaleTrack *st = static_cast<const ScaleTrack *>(t);
@@ -2826,7 +3041,6 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
}
-
} break;
case TYPE_BLEND_SHAPE: {
const BlendShapeTrack *bst = static_cast<const BlendShapeTrack *>(t);
@@ -2835,32 +3049,26 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
} else {
_track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
}
-
} break;
case TYPE_VALUE: {
const ValueTrack *vt = static_cast<const ValueTrack *>(t);
_track_get_key_indices_in_range(vt->values, from_time, to_time, p_indices);
-
} break;
case TYPE_METHOD: {
const MethodTrack *mt = static_cast<const MethodTrack *>(t);
_track_get_key_indices_in_range(mt->methods, from_time, to_time, p_indices);
-
} break;
case TYPE_BEZIER: {
const BezierTrack *bz = static_cast<const BezierTrack *>(t);
_track_get_key_indices_in_range(bz->values, from_time, to_time, p_indices);
-
} break;
case TYPE_AUDIO: {
const AudioTrack *ad = static_cast<const AudioTrack *>(t);
_track_get_key_indices_in_range(ad->values, from_time, to_time, p_indices);
-
} break;
case TYPE_ANIMATION: {
const AnimationTrack *an = static_cast<const AnimationTrack *>(t);
_track_get_key_indices_in_range(an->values, from_time, to_time, p_indices);
-
} break;
}
}
@@ -2898,7 +3106,7 @@ void Animation::_method_track_get_key_indices_in_range(const MethodTrack *mt, do
}
}
-void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const {
+void Animation::method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged) const {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
ERR_FAIL_COND(t->type != TYPE_METHOD);
@@ -2912,35 +3120,58 @@ void Animation::method_track_get_key_indices(int p_track, double p_time, double
SWAP(from_time, to_time);
}
- if (loop) {
- if (from_time > length || from_time < 0) {
- from_time = Math::fposmod(from_time, length);
- }
+ switch (loop_mode) {
+ case LOOP_NONE: {
+ if (from_time < 0) {
+ from_time = 0;
+ }
+ if (from_time > length) {
+ from_time = length;
+ }
- if (to_time > length || to_time < 0) {
- to_time = Math::fposmod(to_time, length);
- }
+ if (to_time < 0) {
+ to_time = 0;
+ }
+ if (to_time > length) {
+ to_time = length;
+ }
+ } break;
+ case LOOP_LINEAR: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::fposmod(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::fposmod(to_time, length);
+ }
- if (from_time > to_time) {
- // handle loop by splitting
- _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
- _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
- return;
- }
- } else {
- if (from_time < 0) {
- from_time = 0;
- }
- if (from_time > length) {
- from_time = length;
- }
+ if (from_time > to_time) {
+ // handle loop by splitting
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ } break;
+ case LOOP_PINGPONG: {
+ if (from_time > length || from_time < 0) {
+ from_time = Math::pingpong(from_time, length);
+ }
+ if (to_time > length || to_time < 0) {
+ to_time = Math::pingpong(to_time, length);
+ }
- if (to_time < 0) {
- to_time = 0;
- }
- if (to_time > length) {
- to_time = length;
- }
+ if (p_pingponged == -1) {
+ _method_track_get_key_indices_in_range(mt, 0, from_time, p_indices);
+ _method_track_get_key_indices_in_range(mt, 0, to_time, p_indices);
+ return;
+ }
+ if (p_pingponged == 1) {
+ _method_track_get_key_indices_in_range(mt, from_time, length, p_indices);
+ _method_track_get_key_indices_in_range(mt, to_time, length, p_indices);
+ return;
+ }
+ } break;
+ default:
+ break;
}
_method_track_get_key_indices_in_range(mt, from_time, to_time, p_indices);
@@ -3326,13 +3557,13 @@ real_t Animation::get_length() const {
return length;
}
-void Animation::set_loop(bool p_enabled) {
- loop = p_enabled;
+void Animation::set_loop_mode(Animation::LoopMode p_loop_mode) {
+ loop_mode = p_loop_mode;
emit_changed();
}
-bool Animation::has_loop() const {
- return loop;
+Animation::LoopMode Animation::get_loop_mode() const {
+ return loop_mode;
}
void Animation::track_set_imported(int p_track, bool p_imported) {
@@ -3514,8 +3745,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
- ClassDB::bind_method(D_METHOD("set_loop", "enabled"), &Animation::set_loop);
- ClassDB::bind_method(D_METHOD("has_loop"), &Animation::has_loop);
+ ClassDB::bind_method(D_METHOD("set_loop_mode", "loop_mode"), &Animation::set_loop_mode);
+ ClassDB::bind_method(D_METHOD("get_loop_mode"), &Animation::get_loop_mode);
ClassDB::bind_method(D_METHOD("set_step", "size_sec"), &Animation::set_step);
ClassDB::bind_method(D_METHOD("get_step"), &Animation::get_step);
@@ -3526,7 +3757,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "length", PROPERTY_HINT_RANGE, "0.001,99999,0.001"), "set_length", "get_length");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode"), "set_loop_mode", "get_loop_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
ADD_SIGNAL(MethodInfo("tracks_changed"));
@@ -3549,6 +3780,10 @@ void Animation::_bind_methods() {
BIND_ENUM_CONSTANT(UPDATE_DISCRETE);
BIND_ENUM_CONSTANT(UPDATE_TRIGGER);
BIND_ENUM_CONSTANT(UPDATE_CAPTURE);
+
+ BIND_ENUM_CONSTANT(LOOP_NONE);
+ BIND_ENUM_CONSTANT(LOOP_LINEAR);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
}
void Animation::clear() {
@@ -3556,7 +3791,7 @@ void Animation::clear() {
memdelete(tracks[i]);
}
tracks.clear();
- loop = false;
+ loop_mode = LOOP_NONE;
length = 1;
compression.enabled = false;
compression.bounds.clear();
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index ee07fb19d3..510d6c8323 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -64,7 +64,12 @@ public:
UPDATE_DISCRETE,
UPDATE_TRIGGER,
UPDATE_CAPTURE,
+ };
+ enum LoopMode {
+ LOOP_NONE,
+ LOOP_LINEAR,
+ LOOP_PINGPONG,
};
private:
@@ -208,7 +213,8 @@ private:
int _insert(double p_time, T &p_keys, const V &p_value);
template <class K>
- inline int _find(const Vector<K> &p_keys, double p_time) const;
+
+ inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false) const;
_FORCE_INLINE_ Vector3 _interpolate(const Vector3 &p_a, const Vector3 &p_b, real_t p_c) const;
_FORCE_INLINE_ Quaternion _interpolate(const Quaternion &p_a, const Quaternion &p_b, real_t p_c) const;
@@ -221,7 +227,7 @@ private:
_FORCE_INLINE_ real_t _cubic_interpolate(const real_t &p_pre_a, const real_t &p_a, const real_t &p_b, const real_t &p_post_b, real_t p_c) const;
template <class T>
- _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok) const;
+ _FORCE_INLINE_ T _interpolate(const Vector<TKey<T>> &p_keys, double p_time, InterpolationType p_interp, bool p_loop_wrap, bool *p_ok, bool p_backward = false) const;
template <class T>
_FORCE_INLINE_ void _track_get_key_indices_in_range(const Vector<T> &p_array, double from_time, double to_time, List<int> *p_indices) const;
@@ -231,7 +237,8 @@ private:
double length = 1.0;
real_t step = 0.1;
- bool loop = false;
+ LoopMode loop_mode = LOOP_NONE;
+ int pingponged = 0;
/* Animation compression page format (version 1):
*
@@ -438,23 +445,23 @@ public:
bool track_get_interpolation_loop_wrap(int p_track) const;
Variant value_track_interpolate(int p_track, double p_time) const;
- void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void value_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void value_track_set_update_mode(int p_track, UpdateMode p_mode);
UpdateMode value_track_get_update_mode(int p_track) const;
- void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void method_track_get_key_indices(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
Vector<Variant> method_track_get_params(int p_track, int p_key_idx) const;
StringName method_track_get_name(int p_track, int p_key_idx) const;
void copy_track(int p_track, Ref<Animation> p_to_animation);
- void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices) const;
+ void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, int p_pingponged = 0) const;
void set_length(real_t p_length);
real_t get_length() const;
- void set_loop(bool p_enabled);
- bool has_loop() const;
+ void set_loop_mode(LoopMode p_loop_mode);
+ LoopMode get_loop_mode() const;
void set_step(real_t p_step);
real_t get_step() const;
@@ -471,5 +478,6 @@ public:
VARIANT_ENUM_CAST(Animation::TrackType);
VARIANT_ENUM_CAST(Animation::InterpolationType);
VARIANT_ENUM_CAST(Animation::UpdateMode);
+VARIANT_ENUM_CAST(Animation::LoopMode);
#endif
diff --git a/scene/resources/audio_stream_sample.cpp b/scene/resources/audio_stream_sample.cpp
index 5cfc4a9d40..d3fab802c5 100644
--- a/scene/resources/audio_stream_sample.cpp
+++ b/scene/resources/audio_stream_sample.cpp
@@ -299,7 +299,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset < loop_begin_fp) {
/* loopstart reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_begin_fp + (loop_begin_fp - offset);
increment = -increment;
@@ -320,7 +320,7 @@ int AudioStreamPlaybackSample::mix(AudioFrame *p_buffer, float p_rate_scale, int
if (loop_format != AudioStreamSample::LOOP_DISABLED && offset >= loop_end_fp) {
/* loopend reached */
- if (loop_format == AudioStreamSample::LOOP_PING_PONG) {
+ if (loop_format == AudioStreamSample::LOOP_PINGPONG) {
/* bounce ping pong */
offset = loop_end_fp - (offset - loop_end_fp);
increment = -increment;
@@ -650,7 +650,7 @@ void AudioStreamSample::_bind_methods() {
BIND_ENUM_CONSTANT(LOOP_DISABLED);
BIND_ENUM_CONSTANT(LOOP_FORWARD);
- BIND_ENUM_CONSTANT(LOOP_PING_PONG);
+ BIND_ENUM_CONSTANT(LOOP_PINGPONG);
BIND_ENUM_CONSTANT(LOOP_BACKWARD);
}
diff --git a/scene/resources/audio_stream_sample.h b/scene/resources/audio_stream_sample.h
index 24198e3c98..0eb34be9bf 100644
--- a/scene/resources/audio_stream_sample.h
+++ b/scene/resources/audio_stream_sample.h
@@ -92,7 +92,7 @@ public:
enum LoopMode {
LOOP_DISABLED,
LOOP_FORWARD,
- LOOP_PING_PONG,
+ LOOP_PINGPONG,
LOOP_BACKWARD
};
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index f21a070133..a1d76ef352 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -940,7 +940,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
- theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+ theme->set_constant("shadow_outline_size", "RichTextLabel", 1 * scale);
theme->set_constant("line_separation", "RichTextLabel", 0 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 819ae95715..d9de47afc7 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -70,6 +70,15 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_antialiased", "antialiased"), &FontData::set_antialiased);
ClassDB::bind_method(D_METHOD("is_antialiased"), &FontData::is_antialiased);
+ ClassDB::bind_method(D_METHOD("set_font_name", "name"), &FontData::set_font_name);
+ ClassDB::bind_method(D_METHOD("get_font_name"), &FontData::get_font_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style_name", "name"), &FontData::set_font_style_name);
+ ClassDB::bind_method(D_METHOD("get_font_style_name"), &FontData::get_font_style_name);
+
+ ClassDB::bind_method(D_METHOD("set_font_style", "style"), &FontData::set_font_style);
+ ClassDB::bind_method(D_METHOD("get_font_style"), &FontData::get_font_style);
+
ClassDB::bind_method(D_METHOD("set_multichannel_signed_distance_field", "msdf"), &FontData::set_multichannel_signed_distance_field);
ClassDB::bind_method(D_METHOD("is_multichannel_signed_distance_field"), &FontData::is_multichannel_signed_distance_field);
@@ -190,6 +199,15 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
} else if (tokens[0] == "antialiased") {
set_antialiased(p_value);
return true;
+ } else if (tokens[0] == "font_name") {
+ set_font_name(p_value);
+ return true;
+ } else if (tokens[0] == "style_name") {
+ set_font_style_name(p_value);
+ return true;
+ } else if (tokens[0] == "font_style") {
+ set_font_style(p_value);
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
set_multichannel_signed_distance_field(p_value);
return true;
@@ -295,6 +313,15 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
} else if (tokens[0] == "antialiased") {
r_ret = is_antialiased();
return true;
+ } else if (tokens[0] == "font_name") {
+ r_ret = get_font_name();
+ return true;
+ } else if (tokens[0] == "style_name") {
+ r_ret = get_font_style_name();
+ return true;
+ } else if (tokens[0] == "font_style") {
+ r_ret = get_font_style();
+ return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
r_ret = is_multichannel_signed_distance_field();
return true;
@@ -394,6 +421,9 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
+ p_list->push_back(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
@@ -510,6 +540,36 @@ PackedByteArray FontData::get_data() const {
return data;
}
+void FontData::set_font_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_name(cache[0], p_name);
+}
+
+String FontData::get_font_name() const {
+ _ensure_rid(0);
+ return TS->font_get_name(cache[0]);
+}
+
+void FontData::set_font_style_name(const String &p_name) {
+ _ensure_rid(0);
+ TS->font_set_style_name(cache[0], p_name);
+}
+
+String FontData::get_font_style_name() const {
+ _ensure_rid(0);
+ return TS->font_get_style_name(cache[0]);
+}
+
+void FontData::set_font_style(uint32_t p_style) {
+ _ensure_rid(0);
+ TS->font_set_style(cache[0], p_style);
+}
+
+uint32_t FontData::get_font_style() const {
+ _ensure_rid(0);
+ return TS->font_get_style(cache[0]);
+}
+
void FontData::set_antialiased(bool p_antialiased) {
if (antialiased != p_antialiased) {
antialiased = p_antialiased;
diff --git a/scene/resources/font.h b/scene/resources/font.h
index e1f1f6d742..4d9ee72c84 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -79,6 +79,15 @@ public:
virtual PackedByteArray get_data() const;
// Common properties.
+ virtual void set_font_name(const String &p_name);
+ virtual String get_font_name() const;
+
+ virtual void set_font_style_name(const String &p_name);
+ virtual String get_font_style_name() const;
+
+ virtual void set_font_style(uint32_t p_style);
+ virtual uint32_t get_font_style() const;
+
virtual void set_antialiased(bool p_antialiased);
virtual bool is_antialiased() const;
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index fae1de94d3..1b7fc64267 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -38,6 +38,11 @@ void TextParagraph::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "direction", PROPERTY_HINT_ENUM, "Auto,Light-to-right,Right-to-left"), "set_direction", "get_direction");
+ ClassDB::bind_method(D_METHOD("set_custom_punctuation", "custom_punctuation"), &TextParagraph::set_custom_punctuation);
+ ClassDB::bind_method(D_METHOD("get_custom_punctuation"), &TextParagraph::get_custom_punctuation);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "custom_punctuation"), "set_custom_punctuation", "get_custom_punctuation");
+
ClassDB::bind_method(D_METHOD("set_orientation", "orientation"), &TextParagraph::set_orientation);
ClassDB::bind_method(D_METHOD("get_orientation"), &TextParagraph::get_orientation);
@@ -304,6 +309,15 @@ TextServer::Direction TextParagraph::get_direction() const {
return TS->shaped_text_get_direction(rid);
}
+void TextParagraph::set_custom_punctuation(const String &p_punct) {
+ TS->shaped_text_set_custom_punctuation(rid, p_punct);
+ lines_dirty = true;
+}
+
+String TextParagraph::get_custom_punctuation() const {
+ return TS->shaped_text_get_custom_punctuation(rid);
+}
+
void TextParagraph::set_orientation(TextServer::Orientation p_orientation) {
TS->shaped_text_set_orientation(rid, p_orientation);
TS->shaped_text_set_orientation(dropcap_rid, p_orientation);
diff --git a/scene/resources/text_paragraph.h b/scene/resources/text_paragraph.h
index 701c9a17cd..4c4af43d14 100644
--- a/scene/resources/text_paragraph.h
+++ b/scene/resources/text_paragraph.h
@@ -96,6 +96,9 @@ public:
void set_bidi_override(const Array &p_override);
+ void set_custom_punctuation(const String &p_punct);
+ String get_custom_punctuation() const;
+
bool set_dropcap(const String &p_text, const Ref<Font> &p_fonts, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "");
void clear_dropcap();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 0aa79b8ceb..9e0ff37ab5 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -354,7 +354,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
}
String code = " {\n";
String _code;
- GDVIRTUAL_CALL(_get_code, input_vars, output_vars, (int)p_mode, (int)p_type, _code);
+ GDVIRTUAL_CALL(_get_code, input_vars, output_vars, p_mode, p_type, _code);
bool nend = _code.ends_with("\n");
_code = _code.insert(0, " ");
_code = _code.replace("\n", "\n ");
@@ -371,7 +371,7 @@ String VisualShaderNodeCustom::generate_code(Shader::Mode p_mode, VisualShader::
String VisualShaderNodeCustom::generate_global_per_node(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const {
String ret;
- if (GDVIRTUAL_CALL(_get_global_code, (int)p_mode, ret)) {
+ if (GDVIRTUAL_CALL(_get_global_code, p_mode, ret)) {
String code = "// " + get_caption() + "\n";
code += ret;
code += "\n";
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 7743affba7..7ff404bedc 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -328,8 +328,8 @@ protected:
GDVIRTUAL0RC(int, _get_output_port_count)
GDVIRTUAL1RC(int, _get_output_port_type, int)
GDVIRTUAL1RC(String, _get_output_port_name, int)
- GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, int, int)
- GDVIRTUAL1RC(String, _get_global_code, int)
+ GDVIRTUAL4RC(String, _get_code, Vector<String>, TypedArray<String>, Shader::Mode, VisualShader::Type)
+ GDVIRTUAL1RC(String, _get_global_code, Shader::Mode)
GDVIRTUAL0RC(bool, _is_highend)
protected:
diff --git a/servers/physics_2d/godot_body_2d.cpp b/servers/physics_2d/godot_body_2d.cpp
index 109914d585..442c580920 100644
--- a/servers/physics_2d/godot_body_2d.cpp
+++ b/servers/physics_2d/godot_body_2d.cpp
@@ -221,7 +221,7 @@ Variant GodotBody2D::get_param(PhysicsServer2D::BodyParameter p_param) const {
return inertia;
}
case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: {
- return center_of_mass;
+ return center_of_mass_local;
}
case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: {
return gravity_scale;
diff --git a/servers/physics_2d/godot_body_2d.h b/servers/physics_2d/godot_body_2d.h
index 817d5f1b0d..7b11b50739 100644
--- a/servers/physics_2d/godot_body_2d.h
+++ b/servers/physics_2d/godot_body_2d.h
@@ -282,6 +282,7 @@ public:
void reset_mass_properties();
_FORCE_INLINE_ const Vector2 &get_center_of_mass() const { return center_of_mass; }
+ _FORCE_INLINE_ const Vector2 &get_center_of_mass_local() const { return center_of_mass_local; }
_FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; }
_FORCE_INLINE_ real_t get_inv_inertia() const { return _inv_inertia; }
_FORCE_INLINE_ real_t get_friction() const { return friction; }
diff --git a/servers/physics_2d/godot_body_direct_state_2d.cpp b/servers/physics_2d/godot_body_direct_state_2d.cpp
index deebc57f37..9c9bd56268 100644
--- a/servers/physics_2d/godot_body_direct_state_2d.cpp
+++ b/servers/physics_2d/godot_body_direct_state_2d.cpp
@@ -50,6 +50,10 @@ Vector2 GodotPhysicsDirectBodyState2D::get_center_of_mass() const {
return body->get_center_of_mass();
}
+Vector2 GodotPhysicsDirectBodyState2D::get_center_of_mass_local() const {
+ return body->get_center_of_mass_local();
+}
+
real_t GodotPhysicsDirectBodyState2D::get_inverse_mass() const {
return body->get_inv_mass();
}
diff --git a/servers/physics_2d/godot_body_direct_state_2d.h b/servers/physics_2d/godot_body_direct_state_2d.h
index 2f3e8e5095..ff25205d52 100644
--- a/servers/physics_2d/godot_body_direct_state_2d.h
+++ b/servers/physics_2d/godot_body_direct_state_2d.h
@@ -46,6 +46,7 @@ public:
virtual real_t get_total_linear_damp() const override;
virtual Vector2 get_center_of_mass() const override;
+ virtual Vector2 get_center_of_mass_local() const override;
virtual real_t get_inverse_mass() const override;
virtual real_t get_inverse_inertia() const override;
diff --git a/servers/physics_2d/godot_physics_server_2d.cpp b/servers/physics_2d/godot_physics_server_2d.cpp
index cf66b80076..617fa6470a 100644
--- a/servers/physics_2d/godot_physics_server_2d.cpp
+++ b/servers/physics_2d/godot_physics_server_2d.cpp
@@ -963,10 +963,17 @@ bool GodotPhysicsServer2D::body_test_motion(RID p_body, const MotionParameters &
PhysicsDirectBodyState2D *GodotPhysicsServer2D::body_get_direct_state(RID p_body) {
ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
+ if (!body_owner.owns(p_body)) {
+ return nullptr;
+ }
+
GodotBody2D *body = body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, nullptr);
- ERR_FAIL_COND_V(!body->get_space(), nullptr);
+ if (!body->get_space()) {
+ return nullptr;
+ }
+
ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
return body->get_direct_state();
diff --git a/servers/physics_3d/godot_body_3d.cpp b/servers/physics_3d/godot_body_3d.cpp
index 768c9e6572..acf60d61c2 100644
--- a/servers/physics_3d/godot_body_3d.cpp
+++ b/servers/physics_3d/godot_body_3d.cpp
@@ -267,7 +267,7 @@ Variant GodotBody3D::get_param(PhysicsServer3D::BodyParameter p_param) const {
}
} break;
case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: {
- return center_of_mass;
+ return center_of_mass_local;
} break;
case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: {
return gravity_scale;
diff --git a/servers/physics_3d/godot_body_3d.h b/servers/physics_3d/godot_body_3d.h
index 4993ea6a1b..7fc2f58168 100644
--- a/servers/physics_3d/godot_body_3d.h
+++ b/servers/physics_3d/godot_body_3d.h
@@ -200,6 +200,7 @@ public:
_FORCE_INLINE_ Basis get_principal_inertia_axes() const { return principal_inertia_axes; }
_FORCE_INLINE_ Vector3 get_center_of_mass() const { return center_of_mass; }
+ _FORCE_INLINE_ Vector3 get_center_of_mass_local() const { return center_of_mass_local; }
_FORCE_INLINE_ Vector3 xform_local_to_principal(const Vector3 &p_pos) const { return principal_inertia_axes_local.xform(p_pos - center_of_mass_local); }
_FORCE_INLINE_ void set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; }
diff --git a/servers/physics_3d/godot_body_direct_state_3d.cpp b/servers/physics_3d/godot_body_direct_state_3d.cpp
index 9c234a5752..a929cab6f9 100644
--- a/servers/physics_3d/godot_body_direct_state_3d.cpp
+++ b/servers/physics_3d/godot_body_direct_state_3d.cpp
@@ -49,6 +49,10 @@ Vector3 GodotPhysicsDirectBodyState3D::get_center_of_mass() const {
return body->get_center_of_mass();
}
+Vector3 GodotPhysicsDirectBodyState3D::get_center_of_mass_local() const {
+ return body->get_center_of_mass_local();
+}
+
Basis GodotPhysicsDirectBodyState3D::get_principal_inertia_axes() const {
return body->get_principal_inertia_axes();
}
diff --git a/servers/physics_3d/godot_body_direct_state_3d.h b/servers/physics_3d/godot_body_direct_state_3d.h
index 6c584a2634..35fd1543b0 100644
--- a/servers/physics_3d/godot_body_direct_state_3d.h
+++ b/servers/physics_3d/godot_body_direct_state_3d.h
@@ -46,6 +46,7 @@ public:
virtual real_t get_total_linear_damp() const override;
virtual Vector3 get_center_of_mass() const override;
+ virtual Vector3 get_center_of_mass_local() const override;
virtual Basis get_principal_inertia_axes() const override;
virtual real_t get_inverse_mass() const override;
diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/servers/physics_3d/godot_physics_server_3d.cpp
index 73654939ca..3e7f9faa27 100644
--- a/servers/physics_3d/godot_physics_server_3d.cpp
+++ b/servers/physics_3d/godot_physics_server_3d.cpp
@@ -881,10 +881,17 @@ bool GodotPhysicsServer3D::body_test_motion(RID p_body, const MotionParameters &
PhysicsDirectBodyState3D *GodotPhysicsServer3D::body_get_direct_state(RID p_body) {
ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
+ if (!body_owner.owns(p_body)) {
+ return nullptr;
+ }
+
GodotBody3D *body = body_owner.get_or_null(p_body);
ERR_FAIL_NULL_V(body, nullptr);
- ERR_FAIL_NULL_V(body->get_space(), nullptr);
+ if (!body->get_space()) {
+ return nullptr;
+ }
+
ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification.");
return body->get_direct_state();
diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp
index 466558c9db..fa89ccec65 100644
--- a/servers/physics_server_2d.cpp
+++ b/servers/physics_server_2d.cpp
@@ -78,6 +78,7 @@ void PhysicsDirectBodyState2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_total_angular_damp"), &PhysicsDirectBodyState2D::get_total_angular_damp);
ClassDB::bind_method(D_METHOD("get_center_of_mass"), &PhysicsDirectBodyState2D::get_center_of_mass);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass_local"), &PhysicsDirectBodyState2D::get_center_of_mass_local);
ClassDB::bind_method(D_METHOD("get_inverse_mass"), &PhysicsDirectBodyState2D::get_inverse_mass);
ClassDB::bind_method(D_METHOD("get_inverse_inertia"), &PhysicsDirectBodyState2D::get_inverse_inertia);
@@ -124,6 +125,7 @@ void PhysicsDirectBodyState2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_linear_damp"), "", "get_total_linear_damp");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "total_gravity"), "", "get_total_gravity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass"), "", "get_center_of_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "center_of_mass_local"), "", "get_center_of_mass_local");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleep_state", "is_sleeping");
diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h
index 55ac7e1fdc..fec03a8111 100644
--- a/servers/physics_server_2d.h
+++ b/servers/physics_server_2d.h
@@ -49,6 +49,7 @@ public:
virtual real_t get_total_angular_damp() const = 0; // get density of this body space/area
virtual Vector2 get_center_of_mass() const = 0;
+ virtual Vector2 get_center_of_mass_local() const = 0;
virtual real_t get_inverse_mass() const = 0; // get the mass
virtual real_t get_inverse_inertia() const = 0; // get density of this body space
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index a354b69a29..eb52cbfe5a 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -77,6 +77,7 @@ void PhysicsDirectBodyState3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_total_angular_damp"), &PhysicsDirectBodyState3D::get_total_angular_damp);
ClassDB::bind_method(D_METHOD("get_center_of_mass"), &PhysicsDirectBodyState3D::get_center_of_mass);
+ ClassDB::bind_method(D_METHOD("get_center_of_mass_local"), &PhysicsDirectBodyState3D::get_center_of_mass_local);
ClassDB::bind_method(D_METHOD("get_principal_inertia_axes"), &PhysicsDirectBodyState3D::get_principal_inertia_axes);
ClassDB::bind_method(D_METHOD("get_inverse_mass"), &PhysicsDirectBodyState3D::get_inverse_mass);
@@ -126,6 +127,7 @@ void PhysicsDirectBodyState3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "inverse_inertia"), "", "get_inverse_inertia");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "total_gravity"), "", "get_total_gravity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass"), "", "get_center_of_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "center_of_mass_local"), "", "get_center_of_mass_local");
ADD_PROPERTY(PropertyInfo(Variant::BASIS, "principal_inertia_axes"), "", "get_principal_inertia_axes");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity");
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 8883443e34..b5113fd35f 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -48,6 +48,7 @@ public:
virtual real_t get_total_linear_damp() const = 0;
virtual Vector3 get_center_of_mass() const = 0;
+ virtual Vector3 get_center_of_mass_local() const = 0;
virtual Basis get_principal_inertia_axes() const = 0;
virtual real_t get_inverse_mass() const = 0; // get the mass
virtual Vector3 get_inverse_inertia() const = 0; // get density of this body space
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 04cc844483..d803d5fcf8 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -7439,7 +7439,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
int texture_uniforms = 0;
int texture_binding = 0;
- int uniforms = 0;
int instance_index = 0;
ShaderNode::Uniform::Scope uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
@@ -7790,9 +7789,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
uniform2.texture_order = -1;
- if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) {
- uniform2.order = uniforms++;
- }
}
if (uniform2.array_size > 0) {
@@ -8771,6 +8767,20 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}
+ int uniforms = 0;
+
+ // Need to push arrays to first place in a uniform buffer in order to correct work.
+ for (Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) {
+ if (E->get().texture_order == -1 && E->get().scope != ShaderNode::Uniform::SCOPE_INSTANCE && E->get().array_size > 0) {
+ E->get().order = uniforms++;
+ }
+ }
+ for (Map<StringName, ShaderNode::Uniform>::Element *E = shader->uniforms.front(); E; E = E->next()) {
+ if (E->get().texture_order == -1 && E->get().scope != ShaderNode::Uniform::SCOPE_INSTANCE && E->get().array_size == 0) {
+ E->get().order = uniforms++;
+ }
+ }
+
int error_line;
String error_message;
if (!_check_varying_usages(&error_line, &error_message)) {
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index a44fee7c95..0a7523e33a 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -55,6 +55,15 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_set_data, "font_rid", "data");
GDVIRTUAL_BIND(_font_set_data_ptr, "font_rid", "data_ptr", "data_size");
+ GDVIRTUAL_BIND(_font_set_style, "font_rid", "style");
+ GDVIRTUAL_BIND(_font_get_style, "font_rid");
+
+ GDVIRTUAL_BIND(_font_set_name, "font_rid", "name");
+ GDVIRTUAL_BIND(_font_get_name, "font_rid");
+
+ GDVIRTUAL_BIND(_font_set_style_name, "font_rid", "name_style");
+ GDVIRTUAL_BIND(_font_get_style_name, "font_rid");
+
GDVIRTUAL_BIND(_font_set_antialiased, "font_rid", "antialiased");
GDVIRTUAL_BIND(_font_is_antialiased, "font_rid");
@@ -185,6 +194,9 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_shaped_text_set_bidi_override, "shaped", "override");
+ GDVIRTUAL_BIND(_shaped_text_set_custom_punctuation, "shaped", "punct");
+ GDVIRTUAL_BIND(_shaped_text_get_custom_punctuation, "shaped");
+
GDVIRTUAL_BIND(_shaped_text_set_orientation, "shaped", "orientation");
GDVIRTUAL_BIND(_shaped_text_get_orientation, "shaped");
@@ -368,6 +380,42 @@ void TextServerExtension::font_set_data_ptr(RID p_font_rid, const uint8_t *p_dat
GDVIRTUAL_CALL(_font_set_data_ptr, p_font_rid, p_data_ptr, p_data_size);
}
+void TextServerExtension::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ GDVIRTUAL_CALL(_font_set_style, p_font_rid, p_style);
+}
+
+uint32_t /*FontStyle*/ TextServerExtension::font_get_style(RID p_font_rid) const {
+ uint32_t ret;
+ if (GDVIRTUAL_CALL(_font_get_style, p_font_rid, ret)) {
+ return ret;
+ }
+ return 0;
+}
+
+void TextServerExtension::font_set_style_name(RID p_font_rid, const String &p_name) {
+ GDVIRTUAL_CALL(_font_set_style_name, p_font_rid, p_name);
+}
+
+String TextServerExtension::font_get_style_name(RID p_font_rid) const {
+ String ret;
+ if (GDVIRTUAL_CALL(_font_get_style_name, p_font_rid, ret)) {
+ return ret;
+ }
+ return String();
+}
+
+void TextServerExtension::font_set_name(RID p_font_rid, const String &p_name) {
+ GDVIRTUAL_CALL(_font_set_name, p_font_rid, p_name);
+}
+
+String TextServerExtension::font_get_name(RID p_font_rid) const {
+ String ret;
+ if (GDVIRTUAL_CALL(_font_get_name, p_font_rid, ret)) {
+ return ret;
+ }
+ return String();
+}
+
void TextServerExtension::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
GDVIRTUAL_CALL(_font_set_antialiased, p_font_rid, p_antialiased);
}
@@ -906,6 +954,18 @@ void TextServerExtension::shaped_text_set_bidi_override(RID p_shaped, const Arra
GDVIRTUAL_CALL(_shaped_text_set_bidi_override, p_shaped, p_override);
}
+void TextServerExtension::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ GDVIRTUAL_CALL(_shaped_text_set_custom_punctuation, p_shaped, p_punct);
+}
+
+String TextServerExtension::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ String ret;
+ if (GDVIRTUAL_CALL(_shaped_text_get_custom_punctuation, p_shaped, ret)) {
+ return ret;
+ }
+ return String();
+}
+
void TextServerExtension::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
GDVIRTUAL_CALL(_shaped_text_set_preserve_invalid, p_shaped, p_enabled);
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 954b2cf660..e419b4055d 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -84,6 +84,21 @@ public:
GDVIRTUAL2(_font_set_data, RID, const PackedByteArray &);
GDVIRTUAL3(_font_set_data_ptr, RID, GDNativeConstPtr<const uint8_t>, uint64_t);
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override;
+ GDVIRTUAL2(_font_set_style, RID, uint32_t);
+ GDVIRTUAL1RC(uint32_t, _font_get_style, RID);
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_name(RID p_font_rid) const override;
+ GDVIRTUAL2(_font_set_name, RID, const String &);
+ GDVIRTUAL1RC(String, _font_get_name, RID);
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_style_name(RID p_font_rid) const override;
+ GDVIRTUAL2(_font_set_style_name, RID, const String &);
+ GDVIRTUAL1RC(String, _font_get_style_name, RID);
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
virtual bool font_is_antialiased(RID p_font_rid) const override;
GDVIRTUAL2(_font_set_antialiased, RID, bool);
@@ -301,6 +316,11 @@ public:
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
+ GDVIRTUAL2(_shaped_text_set_custom_punctuation, RID, String);
+ GDVIRTUAL1RC(String, _shaped_text_get_custom_punctuation, RID);
+
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
GDVIRTUAL2(_shaped_text_set_orientation, RID, Orientation);
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index af4718678e..9034239fe0 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -208,6 +208,15 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_set_data", "font_rid", "data"), &TextServer::font_set_data);
+ ClassDB::bind_method(D_METHOD("font_set_style", "font_rid", "style"), &TextServer::font_set_style);
+ ClassDB::bind_method(D_METHOD("font_get_style", "font_rid"), &TextServer::font_get_style);
+
+ ClassDB::bind_method(D_METHOD("font_set_name", "font_rid", "name"), &TextServer::font_set_name);
+ ClassDB::bind_method(D_METHOD("font_get_name", "font_rid"), &TextServer::font_get_name);
+
+ ClassDB::bind_method(D_METHOD("font_set_style_name", "font_rid", "name"), &TextServer::font_set_style_name);
+ ClassDB::bind_method(D_METHOD("font_get_style_name", "font_rid"), &TextServer::font_get_style_name);
+
ClassDB::bind_method(D_METHOD("font_set_antialiased", "font_rid", "antialiased"), &TextServer::font_set_antialiased);
ClassDB::bind_method(D_METHOD("font_is_antialiased", "font_rid"), &TextServer::font_is_antialiased);
@@ -338,6 +347,9 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
+ ClassDB::bind_method(D_METHOD("shaped_text_set_custom_punctuation", "shaped", "punct"), &TextServer::shaped_text_set_custom_punctuation);
+ ClassDB::bind_method(D_METHOD("shaped_text_get_custom_punctuation", "shaped"), &TextServer::shaped_text_get_custom_punctuation);
+
ClassDB::bind_method(D_METHOD("shaped_text_set_orientation", "shaped", "orientation"), &TextServer::shaped_text_set_orientation, DEFVAL(ORIENTATION_HORIZONTAL));
ClassDB::bind_method(D_METHOD("shaped_text_get_orientation", "shaped"), &TextServer::shaped_text_get_orientation);
@@ -470,11 +482,16 @@ void TextServer::_bind_methods() {
BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CONIC);
BIND_ENUM_CONSTANT(CONTOUR_CURVE_TAG_OFF_CUBIC);
- /* Font Spacing*/
+ /* Font Spacing */
BIND_ENUM_CONSTANT(SPACING_GLYPH);
BIND_ENUM_CONSTANT(SPACING_SPACE);
BIND_ENUM_CONSTANT(SPACING_TOP);
BIND_ENUM_CONSTANT(SPACING_BOTTOM);
+
+ /* Font Style */
+ BIND_ENUM_CONSTANT(FONT_BOLD);
+ BIND_ENUM_CONSTANT(FONT_ITALIC);
+ BIND_ENUM_CONSTANT(FONT_FIXED_WIDTH);
}
Vector2 TextServer::get_hex_code_box_size(int p_size, char32_t p_index) const {
diff --git a/servers/text_server.h b/servers/text_server.h
index a5484d8fbd..4f55f881e6 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -125,6 +125,12 @@ public:
SPACING_BOTTOM,
};
+ enum FontStyle {
+ FONT_BOLD = 1 << 0,
+ FONT_ITALIC = 1 << 1,
+ FONT_FIXED_WIDTH = 1 << 2,
+ };
+
void _draw_hex_code_box_number(RID p_canvas, int p_size, const Vector2 &p_pos, uint8_t p_index, const Color &p_color) const;
protected:
@@ -144,6 +150,7 @@ protected:
int end = 0; // Substring end offset in the parent string.
String text;
+ String custom_punct;
TextServer::Direction direction = DIRECTION_LTR; // Desired text direction.
TextServer::Orientation orientation = ORIENTATION_HORIZONTAL;
@@ -224,6 +231,15 @@ public:
virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) = 0;
virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) = 0;
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) = 0;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const = 0;
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) = 0;
+ virtual String font_get_name(RID p_font_rid) const = 0;
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) = 0;
+ virtual String font_get_style_name(RID p_font_rid) const = 0;
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) = 0;
virtual bool font_is_antialiased(RID p_font_rid) const = 0;
@@ -354,6 +370,9 @@ public:
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) = 0;
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) = 0;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const = 0;
+
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) = 0;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const = 0;
@@ -535,6 +554,7 @@ VARIANT_ENUM_CAST(TextServer::Hinting);
VARIANT_ENUM_CAST(TextServer::Feature);
VARIANT_ENUM_CAST(TextServer::ContourPointTag);
VARIANT_ENUM_CAST(TextServer::SpacingType);
+VARIANT_ENUM_CAST(TextServer::FontStyle);
GDVIRTUAL_NATIVE_PTR(Glyph);
GDVIRTUAL_NATIVE_PTR(Glyph *);
diff --git a/tests/SCsub b/tests/SCsub
index 0f3c14f0bd..31466fffc1 100644
--- a/tests/SCsub
+++ b/tests/SCsub
@@ -22,6 +22,11 @@ if env_tests["platform"] == "windows":
if env_tests.msvc:
env_tests.Append(CCFLAGS=["/bigobj"])
+env_tests.add_source_files(env.tests_sources, "core/*.cpp")
+env_tests.add_source_files(env.tests_sources, "core/math/*.cpp")
+env_tests.add_source_files(env.tests_sources, "core/templates/*.cpp")
+env_tests.add_source_files(env.tests_sources, "scene/*.cpp")
+env_tests.add_source_files(env.tests_sources, "servers/*.cpp")
env_tests.add_source_files(env.tests_sources, "*.cpp")
lib = env_tests.add_library("tests", env.tests_sources)
diff --git a/tests/test_config_file.h b/tests/core/io/test_config_file.h
index e3f40e16fd..f6fbaf9a88 100644
--- a/tests/test_config_file.h
+++ b/tests/core/io/test_config_file.h
@@ -32,6 +32,7 @@
#define TEST_CONFIG_FILE_H
#include "core/io/config_file.h"
+#include "core/os/os.h"
#include "tests/test_macros.h"
diff --git a/tests/test_file_access.h b/tests/core/io/test_file_access.h
index b3da16c1d1..4ffc57afe4 100644
--- a/tests/test_file_access.h
+++ b/tests/core/io/test_file_access.h
@@ -32,7 +32,8 @@
#define TEST_FILE_ACCESS_H
#include "core/io/file_access.h"
-#include "test_utils.h"
+#include "tests/test_macros.h"
+#include "tests/test_utils.h"
namespace TestFileAccess {
diff --git a/tests/test_image.h b/tests/core/io/test_image.h
index 99c2a9380d..c4c5f1e18b 100644
--- a/tests/test_image.h
+++ b/tests/core/io/test_image.h
@@ -31,10 +31,10 @@
#ifndef TEST_IMAGE_H
#define TEST_IMAGE_H
-#include "core/io/file_access_pack.h"
#include "core/io/image.h"
-#include "test_utils.h"
+#include "core/os/os.h"
+#include "tests/test_utils.h"
#include "thirdparty/doctest/doctest.h"
namespace TestImage {
diff --git a/tests/test_json.h b/tests/core/io/test_json.h
index 3af58dfa1c..3af58dfa1c 100644
--- a/tests/test_json.h
+++ b/tests/core/io/test_json.h
diff --git a/tests/test_marshalls.h b/tests/core/io/test_marshalls.h
index 6bd916164e..6bd916164e 100644
--- a/tests/test_marshalls.h
+++ b/tests/core/io/test_marshalls.h
diff --git a/tests/test_pck_packer.h b/tests/core/io/test_pck_packer.h
index 06e4e64963..ae51d58522 100644
--- a/tests/test_pck_packer.h
+++ b/tests/core/io/test_pck_packer.h
@@ -35,6 +35,7 @@
#include "core/io/pck_packer.h"
#include "core/os/os.h"
+#include "tests/test_utils.h"
#include "thirdparty/doctest/doctest.h"
namespace TestPCKPacker {
diff --git a/tests/test_resource.h b/tests/core/io/test_resource.h
index cee3281995..cee3281995 100644
--- a/tests/test_resource.h
+++ b/tests/core/io/test_resource.h
diff --git a/tests/test_xml_parser.h b/tests/core/io/test_xml_parser.h
index 55de048d6a..2d00f29ddf 100644
--- a/tests/test_xml_parser.h
+++ b/tests/core/io/test_xml_parser.h
@@ -31,10 +31,7 @@
#ifndef TEST_XML_PARSER_H
#define TEST_XML_PARSER_H
-#include <inttypes.h>
-
#include "core/io/xml_parser.h"
-#include "core/string/ustring.h"
#include "tests/test_macros.h"
diff --git a/tests/test_aabb.h b/tests/core/math/test_aabb.h
index 0312e185a1..b838bed171 100644
--- a/tests/test_aabb.h
+++ b/tests/core/math/test_aabb.h
@@ -32,10 +32,8 @@
#define TEST_AABB_H
#include "core/math/aabb.h"
-#include "core/string/print_string.h"
-#include "tests/test_macros.h"
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
namespace TestAABB {
diff --git a/tests/test_astar.h b/tests/core/math/test_astar.h
index 137c477946..2c183374ac 100644
--- a/tests/test_astar.h
+++ b/tests/core/math/test_astar.h
@@ -32,11 +32,6 @@
#define TEST_ASTAR_H
#include "core/math/a_star.h"
-#include "core/math/math_funcs.h"
-#include "core/os/os.h"
-
-#include <math.h>
-#include <stdio.h>
#include "tests/test_macros.h"
diff --git a/tests/test_basis.h b/tests/core/math/test_basis.h
index b254d9fb7f..500c069a33 100644
--- a/tests/test_basis.h
+++ b/tests/core/math/test_basis.h
@@ -31,9 +31,8 @@
#ifndef TEST_BASIS_H
#define TEST_BASIS_H
+#include "core/math/basis.h"
#include "core/math/random_number_generator.h"
-#include "core/os/os.h"
-#include "core/string/ustring.h"
#include "tests/test_macros.h"
diff --git a/tests/test_color.h b/tests/core/math/test_color.h
index bffa890ae2..82cf786f7a 100644
--- a/tests/test_color.h
+++ b/tests/core/math/test_color.h
@@ -33,7 +33,7 @@
#include "core/math/color.h"
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
namespace TestColor {
diff --git a/tests/test_expression.h b/tests/core/math/test_expression.h
index cb1d29389f..cb1d29389f 100644
--- a/tests/test_expression.h
+++ b/tests/core/math/test_expression.h
diff --git a/tests/test_geometry_2d.h b/tests/core/math/test_geometry_2d.h
index 25af8c355e..8f6669b572 100644
--- a/tests/test_geometry_2d.h
+++ b/tests/core/math/test_geometry_2d.h
@@ -32,7 +32,6 @@
#define TEST_GEOMETRY_2D_H
#include "core/math/geometry_2d.h"
-#include "core/templates/vector.h"
#include "thirdparty/doctest/doctest.h"
diff --git a/tests/test_geometry_3d.h b/tests/core/math/test_geometry_3d.h
index ae30737fe2..f42003ffcf 100644
--- a/tests/test_geometry_3d.h
+++ b/tests/core/math/test_geometry_3d.h
@@ -32,11 +32,7 @@
#define TEST_GEOMETRY_3D_H
#include "core/math/geometry_3d.h"
-#include "core/math/plane.h"
-#include "core/math/random_number_generator.h"
-#include "core/math/vector3.h"
#include "tests/test_macros.h"
-#include "vector"
namespace TestGeometry3D {
TEST_CASE("[Geometry3D] Closest Points Between Segments") {
diff --git a/tests/test_math.cpp b/tests/core/math/test_math.cpp
index 72272382ce..6ec9bc2473 100644
--- a/tests/test_math.cpp
+++ b/tests/core/math/test_math.cpp
@@ -30,23 +30,11 @@
#include "test_math.h"
-#include "core/io/file_access.h"
-#include "core/math/basis.h"
#include "core/math/camera_matrix.h"
#include "core/math/delaunay_3d.h"
#include "core/math/geometry_2d.h"
-#include "core/math/math_funcs.h"
-#include "core/math/transform_3d.h"
-#include "core/os/keyboard.h"
+#include "core/os/main_loop.h"
#include "core/os/os.h"
-#include "core/string/print_string.h"
-#include "core/string/ustring.h"
-#include "core/templates/vmap.h"
-#include "core/variant/method_ptrcall.h"
-#include "core/variant/variant.h"
-#include "scene/main/node.h"
-#include "scene/resources/texture.h"
-#include "servers/rendering/shader_language.h"
namespace TestMath {
diff --git a/tests/test_math.h b/tests/core/math/test_math.h
index 4375925bd5..ab5fb6a050 100644
--- a/tests/test_math.h
+++ b/tests/core/math/test_math.h
@@ -31,7 +31,7 @@
#ifndef TEST_MATH_H
#define TEST_MATH_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestMath {
diff --git a/tests/test_random_number_generator.h b/tests/core/math/test_random_number_generator.h
index 39c4771c19..39c4771c19 100644
--- a/tests/test_random_number_generator.h
+++ b/tests/core/math/test_random_number_generator.h
diff --git a/tests/test_rect2.h b/tests/core/math/test_rect2.h
index 3d9fe5a32e..3d9fe5a32e 100644
--- a/tests/test_rect2.h
+++ b/tests/core/math/test_rect2.h
diff --git a/tests/test_class_db.h b/tests/core/object/test_class_db.h
index 4b058a4c67..4b27905485 100644
--- a/tests/test_class_db.h
+++ b/tests/core/object/test_class_db.h
@@ -31,14 +31,9 @@
#ifndef TEST_CLASS_DB_H
#define TEST_CLASS_DB_H
-#include "core/register_core_types.h"
-
+#include "core/core_bind.h"
#include "core/core_constants.h"
-#include "core/os/os.h"
-#include "core/string/string_name.h"
-#include "core/string/ustring.h"
-#include "core/templates/ordered_hash_map.h"
-#include "core/variant/variant.h"
+#include "core/object/class_db.h"
#include "tests/test_macros.h"
diff --git a/tests/test_method_bind.h b/tests/core/object/test_method_bind.h
index 879e7949e2..c3a869a8a4 100644
--- a/tests/test_method_bind.h
+++ b/tests/core/object/test_method_bind.h
@@ -35,10 +35,6 @@
#include "tests/test_macros.h"
-#include <inttypes.h>
-#include <stdio.h>
-#include <wchar.h>
-
namespace TestMethodBind {
class MethodBindTester : public Object {
diff --git a/tests/test_object.h b/tests/core/object/test_object.h
index 8cb7116a20..4109ea521a 100644
--- a/tests/test_object.h
+++ b/tests/core/object/test_object.h
@@ -32,9 +32,11 @@
#define TEST_OBJECT_H
#include "core/core_string_names.h"
+#include "core/object/class_db.h"
#include "core/object/object.h"
+#include "core/object/script_language.h"
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
// Declared in global namespace because of GDCLASS macro warning (Windows):
// "Unqualified friend declaration referring to type outside of the nearest enclosing namespace
diff --git a/tests/test_node_path.h b/tests/core/string/test_node_path.h
index f30fe53c5a..0216a30f8f 100644
--- a/tests/test_node_path.h
+++ b/tests/core/string/test_node_path.h
@@ -33,7 +33,7 @@
#include "core/string/node_path.h"
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
namespace TestNodePath {
diff --git a/tests/test_string.h b/tests/core/string/test_string.h
index 28d1089d2f..e5b26cf5a4 100644
--- a/tests/test_string.h
+++ b/tests/core/string/test_string.h
@@ -31,15 +31,7 @@
#ifndef TEST_STRING_H
#define TEST_STRING_H
-#include <inttypes.h>
-#include <stdio.h>
-#include <wchar.h>
-
-#include "core/io/ip_address.h"
-#include "core/os/main_loop.h"
-#include "core/os/os.h"
#include "core/string/ustring.h"
-#include "core/variant/variant.h"
#include "tests/test_macros.h"
diff --git a/tests/test_translation.h b/tests/core/string/test_translation.h
index 93c53bbbc9..47e06add40 100644
--- a/tests/test_translation.h
+++ b/tests/core/string/test_translation.h
@@ -39,7 +39,8 @@
#include "editor/import/resource_importer_csv_translation.h"
#endif
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
+#include "tests/test_utils.h"
namespace TestTranslation {
diff --git a/tests/test_command_queue.h b/tests/core/templates/test_command_queue.h
index f0d4569942..5d228f2bf6 100644
--- a/tests/test_command_queue.h
+++ b/tests/core/templates/test_command_queue.h
@@ -33,12 +33,10 @@
#include "core/config/project_settings.h"
#include "core/math/random_number_generator.h"
-#include "core/os/mutex.h"
#include "core/os/os.h"
-#include "core/os/semaphore.h"
#include "core/os/thread.h"
#include "core/templates/command_queue_mt.h"
-#include "test_macros.h"
+#include "tests/test_macros.h"
#if !defined(NO_THREADS)
diff --git a/tests/test_list.h b/tests/core/templates/test_list.h
index 52d5edff70..52d5edff70 100644
--- a/tests/test_list.h
+++ b/tests/core/templates/test_list.h
diff --git a/tests/test_local_vector.h b/tests/core/templates/test_local_vector.h
index eff2a16abc..eff2a16abc 100644
--- a/tests/test_local_vector.h
+++ b/tests/core/templates/test_local_vector.h
diff --git a/tests/test_lru.h b/tests/core/templates/test_lru.h
index 2802754729..9359909c53 100644
--- a/tests/test_lru.h
+++ b/tests/core/templates/test_lru.h
@@ -32,7 +32,6 @@
#define TEST_LRU_H
#include "core/templates/lru.h"
-#include "core/templates/vector.h"
#include "tests/test_macros.h"
diff --git a/tests/test_oa_hash_map.cpp b/tests/core/templates/test_oa_hash_map.cpp
index 904c01642d..904c01642d 100644
--- a/tests/test_oa_hash_map.cpp
+++ b/tests/core/templates/test_oa_hash_map.cpp
diff --git a/tests/test_oa_hash_map.h b/tests/core/templates/test_oa_hash_map.h
index 9745802cc0..f229ac94ea 100644
--- a/tests/test_oa_hash_map.h
+++ b/tests/core/templates/test_oa_hash_map.h
@@ -31,7 +31,7 @@
#ifndef TEST_OA_HASH_MAP_H
#define TEST_OA_HASH_MAP_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestOAHashMap {
diff --git a/tests/test_ordered_hash_map.h b/tests/core/templates/test_ordered_hash_map.h
index fbaaa224cf..35ce0fc656 100644
--- a/tests/test_ordered_hash_map.h
+++ b/tests/core/templates/test_ordered_hash_map.h
@@ -31,10 +31,7 @@
#ifndef TEST_ORDERED_HASH_MAP_H
#define TEST_ORDERED_HASH_MAP_H
-#include "core/os/os.h"
#include "core/templates/ordered_hash_map.h"
-#include "core/templates/pair.h"
-#include "core/templates/vector.h"
#include "tests/test_macros.h"
diff --git a/tests/test_paged_array.h b/tests/core/templates/test_paged_array.h
index 7efd3799f3..7efd3799f3 100644
--- a/tests/test_paged_array.h
+++ b/tests/core/templates/test_paged_array.h
diff --git a/tests/test_vector.h b/tests/core/templates/test_vector.h
index bfdf389aa7..bfdf389aa7 100644
--- a/tests/test_vector.h
+++ b/tests/core/templates/test_vector.h
diff --git a/tests/test_crypto.h b/tests/core/test_crypto.h
index 8da8c75544..3b909c7df8 100644
--- a/tests/test_crypto.h
+++ b/tests/core/test_crypto.h
@@ -33,7 +33,6 @@
#include "core/crypto/crypto.h"
#include "tests/test_macros.h"
-#include <stdio.h>
namespace TestCrypto {
diff --git a/tests/test_hashing_context.h b/tests/core/test_hashing_context.h
index 728a5f2cfa..728a5f2cfa 100644
--- a/tests/test_hashing_context.h
+++ b/tests/core/test_hashing_context.h
diff --git a/tests/test_time.h b/tests/core/test_time.h
index 28f1cb2f20..28f1cb2f20 100644
--- a/tests/test_time.h
+++ b/tests/core/test_time.h
diff --git a/tests/test_array.h b/tests/core/variant/test_array.h
index 05b4eaea2a..298bd2ff63 100644
--- a/tests/test_array.h
+++ b/tests/core/variant/test_array.h
@@ -31,13 +31,7 @@
#ifndef TEST_ARRAY_H
#define TEST_ARRAY_H
-#include "core/object/class_db.h"
-#include "core/object/script_language.h"
-#include "core/templates/hashfuncs.h"
-#include "core/templates/vector.h"
#include "core/variant/array.h"
-#include "core/variant/container_type_validate.h"
-#include "core/variant/variant.h"
#include "tests/test_macros.h"
#include "tests/test_tools.h"
diff --git a/tests/test_dictionary.h b/tests/core/variant/test_dictionary.h
index 64d1d68e21..65079698a3 100644
--- a/tests/test_dictionary.h
+++ b/tests/core/variant/test_dictionary.h
@@ -31,10 +31,7 @@
#ifndef TEST_DICTIONARY_H
#define TEST_DICTIONARY_H
-#include "core/templates/ordered_hash_map.h"
-#include "core/templates/safe_refcount.h"
#include "core/variant/dictionary.h"
-#include "core/variant/variant.h"
#include "tests/test_macros.h"
namespace TestDictionary {
diff --git a/tests/test_variant.h b/tests/core/variant/test_variant.h
index 0d16fa092c..0d16fa092c 100644
--- a/tests/test_variant.h
+++ b/tests/core/variant/test_variant.h
diff --git a/tests/test_code_edit.h b/tests/scene/test_code_edit.h
index 62235ed0ae..202ba8dca5 100644
--- a/tests/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -31,12 +31,7 @@
#ifndef TEST_CODE_EDIT_H
#define TEST_CODE_EDIT_H
-#include "core/input/input_map.h"
-#include "core/object/message_queue.h"
-#include "core/os/keyboard.h"
-#include "core/string/string_builder.h"
#include "scene/gui/code_edit.h"
-#include "scene/resources/default_theme/default_theme.h"
#include "tests/test_macros.h"
diff --git a/tests/test_curve.h b/tests/scene/test_curve.h
index e079905e35..60eafad460 100644
--- a/tests/test_curve.h
+++ b/tests/scene/test_curve.h
@@ -33,7 +33,7 @@
#include "scene/resources/curve.h"
-#include "thirdparty/doctest/doctest.h"
+#include "tests/test_macros.h"
namespace TestCurve {
diff --git a/tests/test_gradient.h b/tests/scene/test_gradient.h
index 8eaa6b2b64..fc595b02f2 100644
--- a/tests/test_gradient.h
+++ b/tests/scene/test_gradient.h
@@ -31,8 +31,6 @@
#ifndef TEST_GRADIENT_H
#define TEST_GRADIENT_H
-#include "core/math/color.h"
-#include "core/object/class_db.h"
#include "scene/resources/gradient.h"
#include "thirdparty/doctest/doctest.h"
diff --git a/tests/test_gui.cpp b/tests/scene/test_gui.cpp
index 0ec8aa78c4..5bd9390cb7 100644
--- a/tests/test_gui.cpp
+++ b/tests/scene/test_gui.cpp
@@ -32,29 +32,18 @@
#include "test_gui.h"
-#include "core/io/image_loader.h"
-#include "core/os/os.h"
-#include "core/string/print_string.h"
-#include "scene/2d/sprite_2d.h"
#include "scene/gui/button.h"
-#include "scene/gui/control.h"
#include "scene/gui/label.h"
#include "scene/gui/line_edit.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel.h"
-#include "scene/gui/popup_menu.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/scroll_bar.h"
#include "scene/gui/spin_box.h"
#include "scene/gui/tab_container.h"
-#include "scene/gui/texture_rect.h"
#include "scene/gui/tree.h"
-#include "scene/main/scene_tree.h"
-
-#include "scene/3d/camera_3d.h"
-#include "scene/main/window.h"
namespace TestGUI {
@@ -267,4 +256,4 @@ MainLoop *test() {
}
} // namespace TestGUI
-#endif
+#endif // _3D_DISABLED
diff --git a/tests/test_gui.h b/tests/scene/test_gui.h
index e5c40de7e8..84bce620e2 100644
--- a/tests/test_gui.h
+++ b/tests/scene/test_gui.h
@@ -31,7 +31,7 @@
#ifndef TEST_GUI_H
#define TEST_GUI_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestGUI {
diff --git a/tests/test_path_3d.h b/tests/scene/test_path_3d.h
index 9961ae6e97..1fcef3adde 100644
--- a/tests/test_path_3d.h
+++ b/tests/scene/test_path_3d.h
@@ -32,7 +32,6 @@
#define TEST_PATH_3D_H
#include "scene/3d/path_3d.h"
-#include "scene/resources/curve.h"
#include "tests/test_macros.h"
diff --git a/tests/test_path_follow_2d.h b/tests/scene/test_path_follow_2d.h
index 388b690060..ddfcc5552a 100644
--- a/tests/test_path_follow_2d.h
+++ b/tests/scene/test_path_follow_2d.h
@@ -32,7 +32,6 @@
#define TEST_PATH_FOLLOW_2D_H
#include "scene/2d/path_2d.h"
-#include "scene/resources/curve.h"
#include "tests/test_macros.h"
diff --git a/tests/test_path_follow_3d.h b/tests/scene/test_path_follow_3d.h
index b6b4c88222..6a505dbb39 100644
--- a/tests/test_path_follow_3d.h
+++ b/tests/scene/test_path_follow_3d.h
@@ -32,7 +32,6 @@
#define TEST_PATH_FOLLOW_3D_H
#include "scene/3d/path_3d.h"
-#include "scene/resources/curve.h"
#include "tests/test_macros.h"
diff --git a/tests/test_physics_2d.cpp b/tests/servers/test_physics_2d.cpp
index f6619db8fd..b6360cde8a 100644
--- a/tests/test_physics_2d.cpp
+++ b/tests/servers/test_physics_2d.cpp
@@ -31,11 +31,6 @@
#include "test_physics_2d.h"
#include "core/os/main_loop.h"
-#include "core/os/os.h"
-#include "core/string/print_string.h"
-#include "core/templates/map.h"
-#include "scene/resources/texture.h"
-#include "servers/display_server.h"
#include "servers/physics_server_2d.h"
#include "servers/rendering_server.h"
diff --git a/tests/test_physics_2d.h b/tests/servers/test_physics_2d.h
index 966d49200a..2ae1053a03 100644
--- a/tests/test_physics_2d.h
+++ b/tests/servers/test_physics_2d.h
@@ -31,7 +31,7 @@
#ifndef TEST_PHYSICS_2D_H
#define TEST_PHYSICS_2D_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestPhysics2D {
diff --git a/tests/test_physics_3d.cpp b/tests/servers/test_physics_3d.cpp
index d839ae26b7..4e483d628d 100644
--- a/tests/test_physics_3d.cpp
+++ b/tests/servers/test_physics_3d.cpp
@@ -31,12 +31,8 @@
#include "test_physics_3d.h"
#include "core/math/convex_hull.h"
-#include "core/math/math_funcs.h"
+#include "core/math/geometry_3d.h"
#include "core/os/main_loop.h"
-#include "core/os/os.h"
-#include "core/string/print_string.h"
-#include "core/templates/map.h"
-#include "servers/display_server.h"
#include "servers/physics_server_3d.h"
#include "servers/rendering_server.h"
diff --git a/tests/test_physics_3d.h b/tests/servers/test_physics_3d.h
index b6b66f350e..b86327cdb4 100644
--- a/tests/test_physics_3d.h
+++ b/tests/servers/test_physics_3d.h
@@ -31,7 +31,7 @@
#ifndef TEST_PHYSICS_H
#define TEST_PHYSICS_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestPhysics3D {
diff --git a/tests/test_render.cpp b/tests/servers/test_render.cpp
index 21b4da9b3b..183f049238 100644
--- a/tests/test_render.cpp
+++ b/tests/servers/test_render.cpp
@@ -31,12 +31,7 @@
#include "test_render.h"
#include "core/math/convex_hull.h"
-#include "core/math/math_funcs.h"
-#include "core/os/keyboard.h"
#include "core/os/main_loop.h"
-#include "core/os/os.h"
-#include "core/string/print_string.h"
-#include "servers/display_server.h"
#include "servers/rendering_server.h"
#define OBJECT_COUNT 50
diff --git a/tests/test_render.h b/tests/servers/test_render.h
index 35bb383773..1d773cb347 100644
--- a/tests/test_render.h
+++ b/tests/servers/test_render.h
@@ -31,11 +31,11 @@
#ifndef TEST_RENDER_H
#define TEST_RENDER_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestRender {
MainLoop *test();
}
-#endif
+#endif // TEST_RENDER_H
diff --git a/tests/test_shader_lang.cpp b/tests/servers/test_shader_lang.cpp
index 5598852f29..0591bf6adf 100644
--- a/tests/test_shader_lang.cpp
+++ b/tests/servers/test_shader_lang.cpp
@@ -30,13 +30,8 @@
#include "test_shader_lang.h"
-#include "core/io/file_access.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
-
-#include "core/string/print_string.h"
-#include "scene/gui/control.h"
-#include "scene/gui/text_edit.h"
#include "servers/rendering/shader_language.h"
typedef ShaderLanguage SL;
diff --git a/tests/test_shader_lang.h b/tests/servers/test_shader_lang.h
index 46a2e6af35..de7ec002b6 100644
--- a/tests/test_shader_lang.h
+++ b/tests/servers/test_shader_lang.h
@@ -31,7 +31,7 @@
#ifndef TEST_SHADER_LANG_H
#define TEST_SHADER_LANG_H
-#include "core/os/main_loop.h"
+class MainLoop;
namespace TestShaderLang {
diff --git a/tests/test_text_server.h b/tests/servers/test_text_server.h
index 4edffe3711..4edffe3711 100644
--- a/tests/test_text_server.h
+++ b/tests/servers/test_text_server.h
diff --git a/tests/test_macros.h b/tests/test_macros.h
index 6968f9df1f..0ec6eb9e7c 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -31,10 +31,8 @@
#ifndef TEST_MACROS_H
#define TEST_MACROS_H
-#include "core/object/callable_method_pointer.h"
-#include "core/object/class_db.h"
-#include "core/string/print_string.h"
-#include "core/templates/map.h"
+#include "core/input/input_map.h"
+#include "core/object/message_queue.h"
#include "core/variant/variant.h"
// See documentation for doctest at:
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 341fb1af61..a09be08de8 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -30,62 +30,62 @@
#include "test_main.h"
-#include "core/templates/list.h"
-
-#include "test_aabb.h"
-#include "test_array.h"
-#include "test_astar.h"
-#include "test_basis.h"
-#include "test_class_db.h"
-#include "test_code_edit.h"
-#include "test_color.h"
-#include "test_command_queue.h"
-#include "test_config_file.h"
-#include "test_crypto.h"
-#include "test_curve.h"
-#include "test_dictionary.h"
-#include "test_expression.h"
-#include "test_file_access.h"
-#include "test_geometry_2d.h"
-#include "test_geometry_3d.h"
-#include "test_gradient.h"
-#include "test_gui.h"
-#include "test_hashing_context.h"
-#include "test_image.h"
-#include "test_json.h"
-#include "test_list.h"
-#include "test_local_vector.h"
-#include "test_lru.h"
-#include "test_marshalls.h"
-#include "test_math.h"
-#include "test_method_bind.h"
-#include "test_node_path.h"
-#include "test_oa_hash_map.h"
-#include "test_object.h"
-#include "test_ordered_hash_map.h"
-#include "test_paged_array.h"
-#include "test_path_3d.h"
-#include "test_pck_packer.h"
-#include "test_physics_2d.h"
-#include "test_physics_3d.h"
-#include "test_random_number_generator.h"
-#include "test_rect2.h"
-#include "test_render.h"
-#include "test_resource.h"
-#include "test_shader_lang.h"
-#include "test_string.h"
-#include "test_text_server.h"
-#include "test_time.h"
-#include "test_translation.h"
-#include "test_validate_testing.h"
-#include "test_variant.h"
-#include "test_vector.h"
-#include "test_xml_parser.h"
+#include "tests/core/io/test_config_file.h"
+#include "tests/core/io/test_file_access.h"
+#include "tests/core/io/test_image.h"
+#include "tests/core/io/test_json.h"
+#include "tests/core/io/test_marshalls.h"
+#include "tests/core/io/test_pck_packer.h"
+#include "tests/core/io/test_resource.h"
+#include "tests/core/io/test_xml_parser.h"
+#include "tests/core/math/test_aabb.h"
+#include "tests/core/math/test_astar.h"
+#include "tests/core/math/test_basis.h"
+#include "tests/core/math/test_color.h"
+#include "tests/core/math/test_expression.h"
+#include "tests/core/math/test_geometry_2d.h"
+#include "tests/core/math/test_geometry_3d.h"
+#include "tests/core/math/test_math.h"
+#include "tests/core/math/test_random_number_generator.h"
+#include "tests/core/math/test_rect2.h"
+#include "tests/core/object/test_class_db.h"
+#include "tests/core/object/test_method_bind.h"
+#include "tests/core/object/test_object.h"
+#include "tests/core/string/test_node_path.h"
+#include "tests/core/string/test_string.h"
+#include "tests/core/string/test_translation.h"
+#include "tests/core/templates/test_command_queue.h"
+#include "tests/core/templates/test_list.h"
+#include "tests/core/templates/test_local_vector.h"
+#include "tests/core/templates/test_lru.h"
+#include "tests/core/templates/test_oa_hash_map.h"
+#include "tests/core/templates/test_ordered_hash_map.h"
+#include "tests/core/templates/test_paged_array.h"
+#include "tests/core/templates/test_vector.h"
+#include "tests/core/test_crypto.h"
+#include "tests/core/test_hashing_context.h"
+#include "tests/core/test_time.h"
+#include "tests/core/variant/test_array.h"
+#include "tests/core/variant/test_dictionary.h"
+#include "tests/core/variant/test_variant.h"
+#include "tests/scene/test_code_edit.h"
+#include "tests/scene/test_curve.h"
+#include "tests/scene/test_gradient.h"
+#include "tests/scene/test_gui.h"
+#include "tests/scene/test_path_3d.h"
+#include "tests/servers/test_physics_2d.h"
+#include "tests/servers/test_physics_3d.h"
+#include "tests/servers/test_render.h"
+#include "tests/servers/test_shader_lang.h"
+#include "tests/servers/test_text_server.h"
+#include "tests/test_validate_testing.h"
#include "modules/modules_tests.gen.h"
#include "tests/test_macros.h"
+#include "scene/resources/default_theme/default_theme.h"
+
int test_main(int argc, char *argv[]) {
bool run_tests = true;
diff --git a/tests/test_tools.h b/tests/test_tools.h
index ec18610f04..e1192458d0 100644
--- a/tests/test_tools.h
+++ b/tests/test_tools.h
@@ -31,8 +31,6 @@
#ifndef TEST_TOOLS_H
#define TEST_TOOLS_H
-#include "core/error/error_macros.h"
-
struct ErrorDetector {
ErrorDetector() {
eh.errfunc = _detect_error;
diff --git a/tests/test_utils.cpp b/tests/test_utils.cpp
index 1666a257a9..890dea3ee1 100644
--- a/tests/test_utils.cpp
+++ b/tests/test_utils.cpp
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "test_utils.h"
+#include "tests/test_utils.h"
#include "core/os/os.h"
diff --git a/tests/test_utils.h b/tests/test_utils.h
index f05ab0bdb1..ccebe2e449 100644
--- a/tests/test_utils.h
+++ b/tests/test_utils.h
@@ -31,7 +31,7 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
-#include "core/string/ustring.h"
+class String;
namespace TestUtils {