summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format95
-rw-r--r--.github/workflows/linux_builds.yml41
-rw-r--r--.github/workflows/static_checks.yml6
-rw-r--r--.gitignore3
-rw-r--r--core/config/project_settings.cpp7
-rw-r--r--core/config/project_settings.h1
-rw-r--r--core/core_bind.h2
-rw-r--r--core/debugger/remote_debugger.cpp4
-rw-r--r--core/input/input.cpp1
-rw-r--r--core/input/input.h1
-rw-r--r--core/io/compression.h2
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/logger.h1
-rw-r--r--core/math/basis.cpp508
-rw-r--r--core/math/basis.h42
-rw-r--r--core/math/convex_hull.cpp2
-rw-r--r--core/math/geometry_2d.h2
-rw-r--r--core/math/geometry_3d.h2
-rw-r--r--core/math/quaternion.cpp13
-rw-r--r--core/math/quaternion.h3
-rw-r--r--core/math/vector3.h27
-rw-r--r--core/object/undo_redo.cpp2
-rw-r--r--core/os/memory.h1
-rw-r--r--core/os/os.cpp9
-rw-r--r--core/string/ustring.cpp6
-rw-r--r--core/variant/binder_common.h1
-rw-r--r--core/variant/variant.cpp4
-rw-r--r--core/variant/variant_call.cpp14
-rw-r--r--core/variant/variant_construct.cpp3
-rw-r--r--core/variant/variant_internal.h6
-rw-r--r--doc/Makefile2
-rw-r--r--doc/classes/Animation.xml15
-rw-r--r--doc/classes/Area2D.xml6
-rw-r--r--doc/classes/Area3D.xml6
-rw-r--r--doc/classes/Basis.xml28
-rw-r--r--doc/classes/Button.xml6
-rw-r--r--doc/classes/CanvasItem.xml6
-rw-r--r--doc/classes/CheckBox.xml5
-rw-r--r--doc/classes/CheckButton.xml3
-rw-r--r--doc/classes/ColorPickerButton.xml3
-rw-r--r--doc/classes/Control.xml2
-rw-r--r--doc/classes/DirectionalLight2D.xml2
-rw-r--r--doc/classes/DirectionalLight3D.xml12
-rw-r--r--doc/classes/DisplayServer.xml2
-rw-r--r--doc/classes/EditorImportPlugin.xml4
-rw-r--r--doc/classes/Font.xml25
-rw-r--r--doc/classes/Input.xml7
-rw-r--r--doc/classes/LinkButton.xml3
-rw-r--r--doc/classes/MenuButton.xml3
-rw-r--r--doc/classes/Node3D.xml32
-rw-r--r--doc/classes/OS.xml4
-rw-r--r--doc/classes/OptionButton.xml3
-rw-r--r--doc/classes/PointLight2D.xml2
-rw-r--r--doc/classes/ProjectSettings.xml29
-rw-r--r--doc/classes/Quaternion.xml13
-rw-r--r--doc/classes/RenderingServer.xml19
-rw-r--r--doc/classes/RichTextLabel.xml8
-rw-r--r--doc/classes/SceneTree.xml2
-rw-r--r--doc/classes/SceneTreeTimer.xml2
-rw-r--r--doc/classes/String.xml4
-rw-r--r--doc/classes/TileMap.xml41
-rw-r--r--doc/classes/Time.xml10
-rw-r--r--doc/classes/Vector3.xml11
-rwxr-xr-xdoc/tools/make_rst.py (renamed from doc/tools/makerst.py)4
-rw-r--r--doc/translations/extract.py4
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp4
-rw-r--r--editor/action_map_editor.cpp10
-rw-r--r--editor/animation_track_editor.cpp177
-rw-r--r--editor/animation_track_editor.h4
-rw-r--r--editor/animation_track_editor_plugins.cpp4
-rw-r--r--editor/code_editor.h2
-rw-r--r--editor/editor_data.h2
-rw-r--r--editor/editor_file_system.cpp3
-rw-r--r--editor/editor_fonts.cpp15
-rw-r--r--editor/editor_help.h3
-rw-r--r--editor/editor_inspector.h4
-rw-r--r--editor/editor_node.cpp6
-rw-r--r--editor/editor_node.h12
-rw-r--r--editor/editor_path.h1
-rw-r--r--editor/editor_properties.cpp3
-rw-r--r--editor/editor_themes.cpp12
-rw-r--r--editor/filesystem_dock.cpp2
-rw-r--r--editor/filesystem_dock.h4
-rw-r--r--editor/find_in_files.cpp69
-rw-r--r--editor/groups_editor.cpp100
-rw-r--r--editor/groups_editor.h15
-rw-r--r--editor/icons/NewKey.svg1
-rw-r--r--editor/import/resource_importer_layered_texture.h2
-rw-r--r--editor/import/resource_importer_scene.cpp29
-rw-r--r--editor/import/resource_importer_scene.h1
-rw-r--r--editor/inspector_dock.cpp4
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_player_editor_plugin.h2
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp6
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h10
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp65
-rw-r--r--editor/plugins/node_3d_editor_plugin.h8
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h1
-rw-r--r--editor/plugins/script_editor_plugin.h4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp268
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h38
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h2
-rw-r--r--editor/plugins/theme_editor_plugin.h1
-rw-r--r--editor/plugins/theme_editor_preview.cpp5
-rw-r--r--editor/plugins/theme_editor_preview.h1
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp15
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp792
-rw-r--r--editor/plugins/tiles/tile_map_editor.h58
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp21
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h2
-rw-r--r--editor/project_settings_editor.h2
-rw-r--r--editor/quick_open.cpp23
-rw-r--r--editor/reparent_dialog.h2
-rw-r--r--editor/scene_tree_dock.h2
-rw-r--r--editor/scene_tree_editor.h3
-rw-r--r--editor/translations/ar.po8
-rw-r--r--editor/translations/ca.po135
-rw-r--r--editor/translations/de.po42
-rw-r--r--editor/translations/es.po47
-rw-r--r--editor/translations/es_AR.po267
-rw-r--r--editor/translations/fa.po36
-rw-r--r--editor/translations/fi.po36
-rw-r--r--editor/translations/fr.po154
-rw-r--r--editor/translations/he.po6
-rw-r--r--editor/translations/ja.po132
-rw-r--r--editor/translations/pt.po90
-rw-r--r--editor/translations/pt_BR.po144
-rw-r--r--editor/translations/ru.po44
-rw-r--r--editor/translations/sv.po11
-rw-r--r--editor/translations/uk.po36
-rw-r--r--editor/translations/zh_CN.po38
-rw-r--r--main/main.cpp6
-rw-r--r--methods.py4
-rw-r--r--misc/dist/osx/editor.entitlements12
-rw-r--r--misc/dist/osx/editor_mono.entitlements18
-rw-r--r--misc/hooks/README.md4
-rwxr-xr-xmisc/hooks/pre-commit2
-rwxr-xr-xmisc/hooks/pre-commit-clang-format10
-rwxr-xr-xmisc/hooks/pre-commit-make-rst (renamed from misc/hooks/pre-commit-makerst)4
-rwxr-xr-xmisc/scripts/clang_format.sh2
-rwxr-xr-xmisc/scripts/compare_extension_api.py11
-rw-r--r--modules/bullet/area_bullet.h1
-rw-r--r--modules/bullet/soft_body_bullet.cpp1
-rw-r--r--modules/csg/csg_shape.cpp1
-rw-r--r--modules/denoise/config.py5
-rw-r--r--modules/fbx/tools/import_utils.cpp24
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp3
-rw-r--r--modules/gdnative/nativescript/nativescript.h3
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.gd2
-rw-r--r--modules/gltf/gltf_document.cpp45
-rw-r--r--modules/gltf/gltf_document.h2
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.h2
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.h2
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h2
-rw-r--r--modules/mono/config.py2
-rw-r--r--modules/mono/csharp_script.cpp16
-rw-r--r--modules/mono/csharp_script.h4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs15
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp2
-rw-r--r--modules/raycast/config.py2
-rw-r--r--modules/regex/config.py2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp1
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/visual_script/visual_script_nodes.h6
-rw-r--r--modules/websocket/wsl_server.h2
-rw-r--r--platform/android/audio_driver_opensl.h4
-rw-r--r--platform/android/export/gradle_export_util.cpp19
-rw-r--r--platform/android/java/build.gradle14
-rw-r--r--platform/android/os_android.cpp2
-rw-r--r--platform/android/os_android.h1
-rw-r--r--platform/javascript/package-lock.json6
-rw-r--r--platform/linuxbsd/detect.py7
-rw-r--r--platform/linuxbsd/display_server_x11.cpp1
-rw-r--r--platform/osx/joypad_osx.h1
-rw-r--r--platform/uwp/os_uwp.h5
-rw-r--r--scene/2d/audio_listener_2d.h2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.cpp1
-rw-r--r--scene/2d/collision_shape_2d.cpp2
-rw-r--r--scene/2d/cpu_particles_2d.h7
-rw-r--r--scene/2d/mesh_instance_2d.cpp4
-rw-r--r--scene/2d/mesh_instance_2d.h1
-rw-r--r--scene/2d/navigation_agent_2d.cpp4
-rw-r--r--scene/2d/physics_body_2d.cpp15
-rw-r--r--scene/2d/physics_body_2d.h2
-rw-r--r--scene/2d/ray_cast_2d.cpp1
-rw-r--r--scene/2d/tile_map.cpp749
-rw-r--r--scene/2d/tile_map.h67
-rw-r--r--scene/3d/area_3d.cpp2
-rw-r--r--scene/3d/audio_listener_3d.h3
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/bone_attachment_3d.cpp2
-rw-r--r--scene/3d/camera_3d.cpp2
-rw-r--r--scene/3d/collision_object_3d.cpp4
-rw-r--r--scene/3d/cpu_particles_3d.cpp3
-rw-r--r--scene/3d/cpu_particles_3d.h7
-rw-r--r--scene/3d/decal.cpp1
-rw-r--r--scene/3d/gpu_particles_3d.cpp2
-rw-r--r--scene/3d/light_3d.cpp36
-rw-r--r--scene/3d/light_3d.h1
-rw-r--r--scene/3d/lightmap_gi.cpp1
-rw-r--r--scene/3d/navigation_agent_3d.cpp4
-rw-r--r--scene/3d/navigation_region_3d.cpp2
-rw-r--r--scene/3d/node_3d.cpp107
-rw-r--r--scene/3d/node_3d.h35
-rw-r--r--scene/3d/path_3d.cpp1
-rw-r--r--scene/3d/physics_body_3d.cpp4
-rw-r--r--scene/3d/reflection_probe.cpp1
-rw-r--r--scene/3d/remote_transform_3d.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp22
-rw-r--r--scene/3d/skeleton_ik_3d.cpp2
-rw-r--r--scene/3d/skeleton_ik_3d.h3
-rw-r--r--scene/3d/soft_dynamic_body_3d.cpp88
-rw-r--r--scene/3d/soft_dynamic_body_3d.h15
-rw-r--r--scene/3d/sprite_3d.cpp4
-rw-r--r--scene/3d/sprite_3d.h6
-rw-r--r--scene/3d/xr_nodes.cpp2
-rw-r--r--scene/animation/animation_player.cpp2
-rw-r--r--scene/animation/animation_player.h1
-rw-r--r--scene/animation/animation_tree.h6
-rw-r--r--scene/audio/audio_stream_player.cpp2
-rw-r--r--scene/gui/button.cpp15
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/control.cpp28
-rw-r--r--scene/gui/control.h4
-rw-r--r--scene/gui/file_dialog.cpp6
-rw-r--r--scene/gui/line_edit.cpp58
-rw-r--r--scene/gui/line_edit.h1
-rw-r--r--scene/gui/link_button.cpp7
-rw-r--r--scene/gui/option_button.cpp6
-rw-r--r--scene/gui/popup.h1
-rw-r--r--scene/gui/rich_text_label.cpp14
-rw-r--r--scene/gui/rich_text_label.h4
-rw-r--r--scene/gui/tab_bar.h1
-rw-r--r--scene/gui/text_edit.cpp62
-rw-r--r--scene/gui/tree.cpp6
-rw-r--r--scene/main/canvas_item.cpp6
-rw-r--r--scene/main/canvas_item.h8
-rw-r--r--scene/main/canvas_layer.cpp8
-rw-r--r--scene/main/scene_tree.h4
-rw-r--r--scene/main/viewport.cpp9
-rw-r--r--scene/resources/animation.cpp1653
-rw-r--r--scene/resources/animation.h95
-rw-r--r--scene/resources/default_theme/default_theme.cpp10
-rw-r--r--scene/resources/font.cpp98
-rw-r--r--scene/resources/font.h28
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--scene/resources/mesh.h1
-rw-r--r--scene/resources/shape_2d.h1
-rw-r--r--scene/resources/skeleton_modification_stack_3d.cpp1
-rw-r--r--scene/resources/theme.cpp4
-rw-r--r--scene/resources/tile_set.cpp181
-rw-r--r--scene/resources/tile_set.h15
-rw-r--r--servers/audio/effects/audio_effect_limiter.h2
-rw-r--r--servers/audio/effects/audio_effect_record.h1
-rw-r--r--servers/audio/effects/audio_effect_reverb.h2
-rw-r--r--servers/display_server.cpp1
-rw-r--r--servers/display_server.h1
-rw-r--r--servers/physics_2d/godot_body_2d.cpp33
-rw-r--r--servers/physics_2d/godot_body_2d.h5
-rw-r--r--servers/physics_3d/godot_body_3d.cpp19
-rw-r--r--servers/physics_3d/godot_body_3d.h4
-rw-r--r--servers/physics_3d/godot_physics_server_3d.cpp2
-rw-r--r--servers/physics_3d/godot_soft_body_3d.cpp1
-rw-r--r--servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp2
-rw-r--r--servers/physics_server_2d_wrap_mt.h2
-rw-r--r--servers/physics_server_3d_wrap_mt.h2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp14
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp6
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl5
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl16
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl8
-rw-r--r--servers/rendering/renderer_scene_cull.cpp4
-rw-r--r--servers/rendering/shader_language.cpp1
-rw-r--r--servers/rendering_server.cpp9
-rw-r--r--servers/rendering_server.h1
-rw-r--r--tests/test_basis.h28
-rw-r--r--thirdparty/README.md3
-rw-r--r--thirdparty/mbedtls/library/base64.c255
-rw-r--r--thirdparty/mbedtls/patches/pr4819-faster-base64.patch341
287 files changed, 5780 insertions, 3047 deletions
diff --git a/.clang-format b/.clang-format
index 1c26421a7f..9ddb19e57c 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,31 +1,41 @@
-# Commented out parameters are those with the same value as base LLVM style
+# Commented out parameters are those with the same value as base LLVM style.
# We can uncomment them if we want to change their value, or enforce the
-# chosen value in case the base style changes (last sync: Clang 6.0.1).
+# chosen value in case the base style changes (last sync: Clang 13.0).
---
### General config, applies to all languages ###
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
-# AlignConsecutiveAssignments: false
-# AlignConsecutiveDeclarations: false
+# AlignArrayOfStructures: None
+# AlignConsecutiveMacros: None
+# AlignConsecutiveAssignments: None
+# AlignConsecutiveBitFields: None
+# AlignConsecutiveDeclarations: None
# AlignEscapedNewlines: Right
-# AlignOperands: true
+# AlignOperands: Align
AlignTrailingComments: false
+# AllowAllArgumentsOnNextLine: true
+# AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
-# AllowShortBlocksOnASingleLine: false
+# AllowShortEnumsOnASingleLine: true
+# AllowShortBlocksOnASingleLine: Never
# AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
-# AllowShortIfStatementsOnASingleLine: false
+# AllowShortLambdasOnASingleLine: All
+# AllowShortIfStatementsOnASingleLine: Never
# AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
-# AlwaysBreakTemplateDeclarations: false
+# AlwaysBreakTemplateDeclarations: MultiLine
+# AttributeMacros:
+# - __capability
# BinPackArguments: true
# BinPackParameters: true
# BraceWrapping:
+# AfterCaseLabel: false
# AfterClass: false
-# AfterControlStatement: false
+# AfterControlStatement: Never
# AfterEnum: false
# AfterFunction: false
# AfterNamespace: false
@@ -35,13 +45,17 @@ AllowShortFunctionsOnASingleLine: Inline
# AfterExternBlock: false
# BeforeCatch: false
# BeforeElse: false
+# BeforeLambdaBody: false
+# BeforeWhile: false
# IndentBraces: false
# SplitEmptyFunction: true
# SplitEmptyRecord: true
# SplitEmptyNamespace: true
# BreakBeforeBinaryOperators: None
+# BreakBeforeConceptDeclarations: true
# BreakBeforeBraces: Attach
# BreakBeforeInheritanceComma: false
+# BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: false
# BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
@@ -53,14 +67,19 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
+# DeriveLineEnding: true
# DerivePointerAlignment: false
# DisableFormat: false
+# EmptyLineAfterAccessModifier: Never
+# EmptyLineBeforeAccessModifier: LogicalBlock
# ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments: true
# ForEachMacros:
# - foreach
# - Q_FOREACH
# - BOOST_FOREACH
+# IfMacros:
+# - KJ_IF_MAYBE
# IncludeBlocks: Preserve
IncludeCategories:
- Regex: '".*"'
@@ -70,13 +89,21 @@ IncludeCategories:
- Regex: '^<.*'
Priority: 3
# IncludeIsMainRegex: '(Test)?$'
+# IncludeIsMainSourceRegex: ''
+# IndentAccessModifiers: false
IndentCaseLabels: true
+# IndentCaseBlocks: false
+# IndentGotoLabels: true
# IndentPPDirectives: None
+# IndentExternBlock: AfterExternBlock
+# IndentRequires: false
IndentWidth: 4
# IndentWrappedFunctionNames: false
+# InsertTrailingCommas: None
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
+# LambdaBodyIndentation: Signature
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
@@ -86,38 +113,72 @@ KeepEmptyLinesAtTheStartOfBlocks: false
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
+# PenaltyBreakTemplateDeclaration: 10
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
+# PenaltyIndentedWhitespace: 0
# PointerAlignment: Right
-# RawStringFormats:
-# - Delimiter: pb
-# Language: TextProto
-# BasedOnStyle: google
+# PPIndentWidth: -1
+# ReferenceAlignment: Pointer
# ReflowComments: true
-# SortIncludes: true
+# ShortNamespaceLines: 1
+# SortIncludes: CaseSensitive
+# SortJavaStaticImport: Before
# SortUsingDeclarations: true
# SpaceAfterCStyleCast: false
+# SpaceAfterLogicalNot: false
# SpaceAfterTemplateKeyword: true
# SpaceBeforeAssignmentOperators: true
+# SpaceBeforeCaseColon: false
+# SpaceBeforeCpp11BracedList: false
+# SpaceBeforeCtorInitializerColon: true
+# SpaceBeforeInheritanceColon: true
# SpaceBeforeParens: ControlStatements
+# SpaceAroundPointerQualifiers: Default
+# SpaceBeforeRangeBasedForLoopColon: true
# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
-# SpacesInAngles: false
+# SpaceInEmptyBlock: false
+# SpaceInEmptyParentheses: false
+# SpacesBeforeTrailingComments: 1
+# SpacesInAngles: Never
+# SpacesInContainerLiterals: true
+# SpacesInConditionalStatement: false
# SpacesInContainerLiterals: true
# SpacesInCStyleCastParentheses: false
+## Godot TODO: We'll want to use a min of 1, but we need to see how to fix
+## our comment capitalization at the same time.
+SpacesInLineCommentPrefix:
+ Minimum: 0
+ Maximum: -1
# SpacesInParentheses: false
# SpacesInSquareBrackets: false
+# SpaceBeforeSquareBrackets: false
+# BitFieldColonSpacing: Both
+# StatementAttributeLikeMacros:
+# - Q_EMIT
+# StatementMacros:
+# - Q_UNUSED
+# - QT_REQUIRE_VERSION
TabWidth: 4
+# UseCRLF: false
UseTab: Always
+# WhitespaceSensitiveMacros:
+# - STRINGIZE
+# - PP_STRINGIZE
+# - BOOST_PP_STRINGIZE
+# - NS_SWIFT_NAME
+# - CF_SWIFT_NAME
---
### C++ specific config ###
Language: Cpp
-Standard: Cpp11
+Standard: c++17
---
### ObjC specific config ###
Language: ObjC
-Standard: Cpp11
+# ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 4
+# ObjCBreakBeforeNestedBlockParam: true
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
---
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 018839b817..f6d11e2efe 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -34,6 +34,7 @@ jobs:
tests: true
sconsflags: use_asan=yes use_ubsan=yes
proj-test: true
+ godot-cpp-test: true
bin: "./bin/godot.linuxbsd.tools.64s"
# Skip 2GiB artifact speeding up action.
artifact: false
@@ -126,6 +127,46 @@ jobs:
VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} 40 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
misc/scripts/check_ci_log.py sanitizers_log.txt
+ # Checkout godot-cpp
+ - name: Checkout godot-cpp
+ if: ${{ matrix.godot-cpp-test }}
+ uses: actions/checkout@v2
+ with:
+ repository: godotengine/godot-cpp
+ submodules: 'recursive'
+ path: 'godot-cpp'
+
+ # Check extension API
+ - name: Check for extension api updates
+ if: ${{ matrix.godot-cpp-test }}
+ run: |
+ echo "Running --dump-extension-api to create extensions api."
+ VK_ICD_FILENAMES=$(pwd)/vk_swiftshader_icd.json DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy --dump-extension-api 2>&1 > /dev/null || true
+ misc/scripts/compare_extension_api.py godot-cpp/godot-headers/extension_api.json extension_api.json
+
+ # Copy new extension API files into place
+ - name: Copy new extension API files into place
+ if: ${{ matrix.godot-cpp-test }}
+ run: |
+ cp -f extension_api.json godot-cpp/godot-headers/
+ cp -f core/extension/gdnative_interface.h godot-cpp/godot-headers/godot/
+
+ # Build godot-cpp library
+ - name: Build godot-cpp library
+ if: ${{ matrix.godot-cpp-test }}
+ run: |
+ cd godot-cpp
+ scons target=${{ matrix.target }} generate_bindings=yes -j2
+ cd ..
+
+ # Build godot-cpp test extension
+ - name: Build godot-cpp test extension
+ if: ${{ matrix.godot-cpp-test }}
+ run: |
+ cd godot-cpp/test
+ scons target=${{ matrix.target }} -j2
+ cd ../..
+
- name: Prepare artifact
if: ${{ matrix.artifact }}
run: |
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index fd2e748076..0758aa90b1 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -22,9 +22,9 @@ jobs:
- name: Install dependencies
run: |
- sudo apt-get install -qq dos2unix recode clang-format-11
+ sudo apt-get install -qq dos2unix recode clang-format-12
sudo update-alternatives --remove-all clang-format
- sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-11 100
+ sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-12 100
sudo pip3 install black==20.8b1 pygments
- name: File formatting checks (file_format.sh)
@@ -48,4 +48,4 @@ jobs:
- name: Documentation checks
run: |
- doc/tools/makerst.py --dry-run doc/classes modules
+ doc/tools/make_rst.py --dry-run doc/classes modules
diff --git a/.gitignore b/.gitignore
index 5b3414fe7e..5a8f6aec40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,9 @@
# Documentation generated by doxygen or from classes.xml
doc/_build/
+# Extension API dump
+extension_api.json
+
# Javascript specific
*.bc
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 562cbbdd27..f37e7f5956 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -42,6 +42,8 @@
#include "core/os/os.h"
#include "core/variant/variant_parser.h"
+const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot";
+
ProjectSettings *ProjectSettings::singleton = nullptr;
ProjectSettings *ProjectSettings::get_singleton() {
@@ -521,7 +523,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
}
// Updating the default value after the project settings have loaded.
- project_data_dir_name = GLOBAL_GET("application/config/project_data_dir_name");
+ bool use_hidden_directory = GLOBAL_GET("application/config/use_hidden_project_data_directory");
+ project_data_dir_name = (use_hidden_directory ? "." : "") + PROJECT_DATA_DIR_NAME_SUFFIX;
// Using GLOBAL_GET on every block for compressing can be slow, so assigning here.
Compression::zstd_long_distance_matching = GLOBAL_GET("compression/formats/zstd/long_distance_matching");
@@ -1094,7 +1097,7 @@ ProjectSettings::ProjectSettings() {
custom_prop_info["application/run/main_scene"] = PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res");
GLOBAL_DEF("application/run/disable_stdout", false);
GLOBAL_DEF("application/run/disable_stderr", false);
- project_data_dir_name = GLOBAL_DEF_RST("application/config/project_data_dir_name", ".godot");
+ GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true);
GLOBAL_DEF("application/config/use_custom_user_dir", false);
GLOBAL_DEF("application/config/custom_user_dir_name", "");
GLOBAL_DEF("application/config/project_settings_override", "");
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index 82f04b94df..ca37401751 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -42,6 +42,7 @@ class ProjectSettings : public Object {
public:
typedef Map<String, Variant> CustomMap;
+ static const String PROJECT_DATA_DIR_NAME_SUFFIX;
enum {
//properties that are not for built in values begin from this value, so builtin ones are displayed first
diff --git a/core/core_bind.h b/core/core_bind.h
index 4eab085dda..3eb4c914a1 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -205,7 +205,7 @@ public:
void delay_usec(int p_usec) const;
void delay_msec(int p_msec) const;
- uint32_t get_ticks_msec() const;
+ uint64_t get_ticks_msec() const;
uint64_t get_ticks_usec() const;
bool can_use_threads() const;
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index 9967d1e361..4607bd2f3f 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -79,8 +79,8 @@ public:
ERR_FAIL_COND_V(p_buffer.size() == 0, 0);
int total_bandwidth = 0;
- uint32_t timestamp = OS::get_singleton()->get_ticks_msec();
- uint32_t final_timestamp = timestamp - 1000;
+ uint64_t timestamp = OS::get_singleton()->get_ticks_msec();
+ uint64_t final_timestamp = timestamp - 1000;
int i = (p_pointer + p_buffer.size() - 1) % p_buffer.size();
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 296aa1f071..c3b43a4274 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -133,6 +133,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input);
+ ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/core/input/input.h b/core/input/input.h
index 6819fc8eb0..f63138a875 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -265,7 +265,6 @@ public:
float get_joy_vibration_duration(int p_device);
uint64_t get_joy_vibration_timestamp(int p_device);
void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "");
- void parse_joypad_mapping(String p_mapping, bool p_update_existing);
Vector3 get_gravity() const;
Vector3 get_accelerometer() const;
diff --git a/core/io/compression.h b/core/io/compression.h
index cbfed74124..06f26876e5 100644
--- a/core/io/compression.h
+++ b/core/io/compression.h
@@ -54,8 +54,6 @@ public:
static int get_max_compressed_buffer_size(int p_src_size, Mode p_mode = MODE_ZSTD);
static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD);
static int decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode);
-
- Compression() {}
};
#endif // COMPRESSION_H
diff --git a/core/io/image.cpp b/core/io/image.cpp
index c70f4b86bd..b82e6637b4 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -797,7 +797,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict
uint32_t interp_down = p01 + (((p11 - p01) * src_xofs_frac) >> FRAC_BITS);
uint32_t interp = interp_up + (((interp_down - interp_up) * src_yofs_frac) >> FRAC_BITS);
interp >>= FRAC_BITS;
- p_dst[i * p_dst_width * CC + j * CC + l] = interp;
+ p_dst[i * p_dst_width * CC + j * CC + l] = uint8_t(interp);
} else if (sizeof(T) == 2) { //half float
float xofs_frac = float(src_xofs_frac) / (1 << FRAC_BITS);
diff --git a/core/io/logger.h b/core/io/logger.h
index f244f70e7e..48b073aa45 100644
--- a/core/io/logger.h
+++ b/core/io/logger.h
@@ -83,7 +83,6 @@ class RotatedFileLogger : public Logger {
FileAccess *file = nullptr;
- void rotate_file_without_closing();
void close_file();
void clear_old_backups();
void rotate_file();
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index a7f89522d7..0030cb1144 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -354,7 +354,7 @@ void Basis::rotate(const Quaternion &p_quaternion) {
*this = rotated(p_quaternion);
}
-Vector3 Basis::get_rotation_euler() const {
+Vector3 Basis::get_euler_normalized(EulerOrder p_order) const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
// See the comment in get_scale() for further information.
@@ -365,7 +365,7 @@ Vector3 Basis::get_rotation_euler() const {
m.scale(Vector3(-1, -1, -1));
}
- return m.get_euler();
+ return m.get_euler(p_order);
}
Quaternion Basis::get_rotation_quaternion() const {
@@ -424,218 +424,203 @@ void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) cons
p_angle = -p_angle;
}
-// get_euler_xyz returns a vector containing the Euler angles in the format
-// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last
-// (following the convention they are commonly defined in the literature).
-//
-// The current implementation uses XYZ convention (Z is the first rotation),
-// so euler.z is the angle of the (first) rotation around Z axis and so on,
-//
-// And thus, assuming the matrix is a rotation matrix, this function returns
-// the angles in the decomposition R = X(a1).Y(a2).Z(a3) where Z(a) rotates
-// around the z-axis by a and so on.
-Vector3 Basis::get_euler_xyz() const {
- // Euler angles in XYZ convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cy*cz -cy*sz sy
- // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
- // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
-
- Vector3 euler;
- real_t sy = elements[0][2];
- if (sy < (1.0 - CMP_EPSILON)) {
- if (sy > -(1.0 - CMP_EPSILON)) {
- // is this a pure Y rotation?
- if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) {
- // return the simplest form (human friendlier in editor and scripts)
- euler.x = 0;
- euler.y = atan2(elements[0][2], elements[0][0]);
- euler.z = 0;
+Vector3 Basis::get_euler(EulerOrder p_order) const {
+ switch (p_order) {
+ case EULER_ORDER_XYZ: {
+ // Euler angles in XYZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz -cy*sz sy
+ // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+
+ Vector3 euler;
+ real_t sy = elements[0][2];
+ if (sy < (1.0 - CMP_EPSILON)) {
+ if (sy > -(1.0 - CMP_EPSILON)) {
+ // is this a pure Y rotation?
+ if (elements[1][0] == 0.0 && elements[0][1] == 0.0 && elements[1][2] == 0 && elements[2][1] == 0 && elements[1][1] == 1) {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = 0;
+ euler.y = atan2(elements[0][2], elements[0][0]);
+ euler.z = 0;
+ } else {
+ euler.x = Math::atan2(-elements[1][2], elements[2][2]);
+ euler.y = Math::asin(sy);
+ euler.z = Math::atan2(-elements[0][1], elements[0][0]);
+ }
+ } else {
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
+ euler.y = -Math_PI / 2.0;
+ euler.z = 0.0;
+ }
} else {
- euler.x = Math::atan2(-elements[1][2], elements[2][2]);
- euler.y = Math::asin(sy);
- euler.z = Math::atan2(-elements[0][1], elements[0][0]);
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
+ euler.y = Math_PI / 2.0;
+ euler.z = 0.0;
+ }
+ return euler;
+ } break;
+ case EULER_ORDER_XZY: {
+ // Euler angles in XZY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy -sz cz*sy
+ // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx
+ // cy*sx*sz cz*sx cx*cy+sx*sz*sy
+
+ Vector3 euler;
+ real_t sz = elements[0][1];
+ if (sz < (1.0 - CMP_EPSILON)) {
+ if (sz > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(elements[2][1], elements[1][1]);
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
+ euler.z = Math::asin(-sz);
+ } else {
+ // It's -1
+ euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = Math_PI / 2.0;
+ }
+ } else {
+ // It's 1
+ euler.x = -Math::atan2(elements[1][2], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = -Math_PI / 2.0;
+ }
+ return euler;
+ } break;
+ case EULER_ORDER_YXZ: {
+ // Euler angles in YXZ convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
+ // cx*sz cx*cz -sx
+ // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
+
+ Vector3 euler;
+
+ real_t m12 = elements[1][2];
+
+ if (m12 < (1 - CMP_EPSILON)) {
+ if (m12 > -(1 - CMP_EPSILON)) {
+ // is this a pure X rotation?
+ if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) {
+ // return the simplest form (human friendlier in editor and scripts)
+ euler.x = atan2(-m12, elements[1][1]);
+ euler.y = 0;
+ euler.z = 0;
+ } else {
+ euler.x = asin(-m12);
+ euler.y = atan2(elements[0][2], elements[2][2]);
+ euler.z = atan2(elements[1][0], elements[1][1]);
+ }
+ } else { // m12 == -1
+ euler.x = Math_PI * 0.5;
+ euler.y = atan2(elements[0][1], elements[0][0]);
+ euler.z = 0;
+ }
+ } else { // m12 == 1
+ euler.x = -Math_PI * 0.5;
+ euler.y = -atan2(elements[0][1], elements[0][0]);
+ euler.z = 0;
}
- } else {
- euler.x = Math::atan2(elements[2][1], elements[1][1]);
- euler.y = -Math_PI / 2.0;
- euler.z = 0.0;
- }
- } else {
- euler.x = Math::atan2(elements[2][1], elements[1][1]);
- euler.y = Math_PI / 2.0;
- euler.z = 0.0;
- }
- return euler;
-}
-
-// set_euler_xyz expects a vector containing the Euler angles in the format
-// (ax,ay,az), where ax is the angle of rotation around x axis,
-// and similar for other axes.
-// The current implementation uses XYZ convention (Z is the first rotation).
-void Basis::set_euler_xyz(const Vector3 &p_euler) {
- real_t c, s;
-
- c = Math::cos(p_euler.x);
- s = Math::sin(p_euler.x);
- Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
-
- c = Math::cos(p_euler.y);
- s = Math::sin(p_euler.y);
- Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
-
- c = Math::cos(p_euler.z);
- s = Math::sin(p_euler.z);
- Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
-
- //optimizer will optimize away all this anyway
- *this = xmat * (ymat * zmat);
-}
-
-Vector3 Basis::get_euler_xzy() const {
- // Euler angles in XZY convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cz*cy -sz cz*sy
- // sx*sy+cx*cy*sz cx*cz cx*sz*sy-cy*sx
- // cy*sx*sz cz*sx cx*cy+sx*sz*sy
-
- Vector3 euler;
- real_t sz = elements[0][1];
- if (sz < (1.0 - CMP_EPSILON)) {
- if (sz > -(1.0 - CMP_EPSILON)) {
- euler.x = Math::atan2(elements[2][1], elements[1][1]);
- euler.y = Math::atan2(elements[0][2], elements[0][0]);
- euler.z = Math::asin(-sz);
- } else {
- // It's -1
- euler.x = -Math::atan2(elements[1][2], elements[2][2]);
- euler.y = 0.0;
- euler.z = Math_PI / 2.0;
- }
- } else {
- // It's 1
- euler.x = -Math::atan2(elements[1][2], elements[2][2]);
- euler.y = 0.0;
- euler.z = -Math_PI / 2.0;
- }
- return euler;
-}
-
-void Basis::set_euler_xzy(const Vector3 &p_euler) {
- real_t c, s;
-
- c = Math::cos(p_euler.x);
- s = Math::sin(p_euler.x);
- Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
-
- c = Math::cos(p_euler.y);
- s = Math::sin(p_euler.y);
- Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
-
- c = Math::cos(p_euler.z);
- s = Math::sin(p_euler.z);
- Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
-
- *this = xmat * zmat * ymat;
-}
-
-Vector3 Basis::get_euler_yzx() const {
- // Euler angles in YZX convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
- // sz cz*cx -cz*sx
- // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
-
- Vector3 euler;
- real_t sz = elements[1][0];
- if (sz < (1.0 - CMP_EPSILON)) {
- if (sz > -(1.0 - CMP_EPSILON)) {
- euler.x = Math::atan2(-elements[1][2], elements[1][1]);
- euler.y = Math::atan2(-elements[2][0], elements[0][0]);
- euler.z = Math::asin(sz);
- } else {
- // It's -1
- euler.x = Math::atan2(elements[2][1], elements[2][2]);
- euler.y = 0.0;
- euler.z = -Math_PI / 2.0;
- }
- } else {
- // It's 1
- euler.x = Math::atan2(elements[2][1], elements[2][2]);
- euler.y = 0.0;
- euler.z = Math_PI / 2.0;
- }
- return euler;
-}
-
-void Basis::set_euler_yzx(const Vector3 &p_euler) {
- real_t c, s;
-
- c = Math::cos(p_euler.x);
- s = Math::sin(p_euler.x);
- Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
-
- c = Math::cos(p_euler.y);
- s = Math::sin(p_euler.y);
- Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
-
- c = Math::cos(p_euler.z);
- s = Math::sin(p_euler.z);
- Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
-
- *this = ymat * zmat * xmat;
-}
-
-// get_euler_yxz returns a vector containing the Euler angles in the YXZ convention,
-// as in first-Z, then-X, last-Y. The angles for X, Y, and Z rotations are returned
-// as the x, y, and z components of a Vector3 respectively.
-Vector3 Basis::get_euler_yxz() const {
- // Euler angles in YXZ convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cy*cz+sy*sx*sz cz*sy*sx-cy*sz cx*sy
- // cx*sz cx*cz -sx
- // cy*sx*sz-cz*sy cy*cz*sx+sy*sz cy*cx
-
- Vector3 euler;
-
- real_t m12 = elements[1][2];
- if (m12 < (1 - CMP_EPSILON)) {
- if (m12 > -(1 - CMP_EPSILON)) {
- // is this a pure X rotation?
- if (elements[1][0] == 0 && elements[0][1] == 0 && elements[0][2] == 0 && elements[2][0] == 0 && elements[0][0] == 1) {
- // return the simplest form (human friendlier in editor and scripts)
- euler.x = atan2(-m12, elements[1][1]);
- euler.y = 0;
+ return euler;
+ } break;
+ case EULER_ORDER_YZX: {
+ // Euler angles in YZX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cy*cz sy*sx-cy*cx*sz cx*sy+cy*sz*sx
+ // sz cz*cx -cz*sx
+ // -cz*sy cy*sx+cx*sy*sz cy*cx-sy*sz*sx
+
+ Vector3 euler;
+ real_t sz = elements[1][0];
+ if (sz < (1.0 - CMP_EPSILON)) {
+ if (sz > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(-elements[1][2], elements[1][1]);
+ euler.y = Math::atan2(-elements[2][0], elements[0][0]);
+ euler.z = Math::asin(sz);
+ } else {
+ // It's -1
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = -Math_PI / 2.0;
+ }
+ } else {
+ // It's 1
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = 0.0;
+ euler.z = Math_PI / 2.0;
+ }
+ return euler;
+ } break;
+ case EULER_ORDER_ZXY: {
+ // Euler angles in ZXY convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
+ // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx
+ // -cx*sy sx cx*cy
+ Vector3 euler;
+ real_t sx = elements[2][1];
+ if (sx < (1.0 - CMP_EPSILON)) {
+ if (sx > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::asin(sx);
+ euler.y = Math::atan2(-elements[2][0], elements[2][2]);
+ euler.z = Math::atan2(-elements[0][1], elements[1][1]);
+ } else {
+ // It's -1
+ euler.x = -Math_PI / 2.0;
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
+ euler.z = 0;
+ }
+ } else {
+ // It's 1
+ euler.x = Math_PI / 2.0;
+ euler.y = Math::atan2(elements[0][2], elements[0][0]);
euler.z = 0;
+ }
+ return euler;
+ } break;
+ case EULER_ORDER_ZYX: {
+ // Euler angles in ZYX convention.
+ // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
+ //
+ // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy
+ // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
+ // -sy cy*sx cy*cx
+ Vector3 euler;
+ real_t sy = elements[2][0];
+ if (sy < (1.0 - CMP_EPSILON)) {
+ if (sy > -(1.0 - CMP_EPSILON)) {
+ euler.x = Math::atan2(elements[2][1], elements[2][2]);
+ euler.y = Math::asin(-sy);
+ euler.z = Math::atan2(elements[1][0], elements[0][0]);
+ } else {
+ // It's -1
+ euler.x = 0;
+ euler.y = Math_PI / 2.0;
+ euler.z = -Math::atan2(elements[0][1], elements[1][1]);
+ }
} else {
- euler.x = asin(-m12);
- euler.y = atan2(elements[0][2], elements[2][2]);
- euler.z = atan2(elements[1][0], elements[1][1]);
+ // It's 1
+ euler.x = 0;
+ euler.y = -Math_PI / 2.0;
+ euler.z = -Math::atan2(elements[0][1], elements[1][1]);
}
- } else { // m12 == -1
- euler.x = Math_PI * 0.5;
- euler.y = atan2(elements[0][1], elements[0][0]);
- euler.z = 0;
+ return euler;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(Vector3(), "Invalid parameter for get_euler(order)");
}
- } else { // m12 == 1
- euler.x = -Math_PI * 0.5;
- euler.y = -atan2(elements[0][1], elements[0][0]);
- euler.z = 0;
}
-
- return euler;
+ return Vector3();
}
-// set_euler_yxz expects a vector containing the Euler angles in the format
-// (ax,ay,az), where ax is the angle of rotation around x axis,
-// and similar for other axes.
-// The current implementation uses YXZ convention (Z is the first rotation).
-void Basis::set_euler_yxz(const Vector3 &p_euler) {
+void Basis::set_euler(const Vector3 &p_euler, EulerOrder p_order) {
real_t c, s;
c = Math::cos(p_euler.x);
@@ -650,102 +635,29 @@ void Basis::set_euler_yxz(const Vector3 &p_euler) {
s = Math::sin(p_euler.z);
Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
- //optimizer will optimize away all this anyway
- *this = ymat * xmat * zmat;
-}
-
-Vector3 Basis::get_euler_zxy() const {
- // Euler angles in ZXY convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cz*cy-sz*sx*sy -cx*sz cz*sy+cy*sz*sx
- // cy*sz+cz*sx*sy cz*cx sz*sy-cz*cy*sx
- // -cx*sy sx cx*cy
- Vector3 euler;
- real_t sx = elements[2][1];
- if (sx < (1.0 - CMP_EPSILON)) {
- if (sx > -(1.0 - CMP_EPSILON)) {
- euler.x = Math::asin(sx);
- euler.y = Math::atan2(-elements[2][0], elements[2][2]);
- euler.z = Math::atan2(-elements[0][1], elements[1][1]);
- } else {
- // It's -1
- euler.x = -Math_PI / 2.0;
- euler.y = Math::atan2(elements[0][2], elements[0][0]);
- euler.z = 0;
+ switch (p_order) {
+ case EULER_ORDER_XYZ: {
+ *this = xmat * (ymat * zmat);
+ } break;
+ case EULER_ORDER_XZY: {
+ *this = xmat * zmat * ymat;
+ } break;
+ case EULER_ORDER_YXZ: {
+ *this = ymat * xmat * zmat;
+ } break;
+ case EULER_ORDER_YZX: {
+ *this = ymat * zmat * xmat;
+ } break;
+ case EULER_ORDER_ZXY: {
+ *this = zmat * xmat * ymat;
+ } break;
+ case EULER_ORDER_ZYX: {
+ *this = zmat * ymat * xmat;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid order parameter for set_euler(vec3,order)");
}
- } else {
- // It's 1
- euler.x = Math_PI / 2.0;
- euler.y = Math::atan2(elements[0][2], elements[0][0]);
- euler.z = 0;
}
- return euler;
-}
-
-void Basis::set_euler_zxy(const Vector3 &p_euler) {
- real_t c, s;
-
- c = Math::cos(p_euler.x);
- s = Math::sin(p_euler.x);
- Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
-
- c = Math::cos(p_euler.y);
- s = Math::sin(p_euler.y);
- Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
-
- c = Math::cos(p_euler.z);
- s = Math::sin(p_euler.z);
- Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
-
- *this = zmat * xmat * ymat;
-}
-
-Vector3 Basis::get_euler_zyx() const {
- // Euler angles in ZYX convention.
- // See https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix
- //
- // rot = cz*cy cz*sy*sx-cx*sz sz*sx+cz*cx*cy
- // cy*sz cz*cx+sz*sy*sx cx*sz*sy-cz*sx
- // -sy cy*sx cy*cx
- Vector3 euler;
- real_t sy = elements[2][0];
- if (sy < (1.0 - CMP_EPSILON)) {
- if (sy > -(1.0 - CMP_EPSILON)) {
- euler.x = Math::atan2(elements[2][1], elements[2][2]);
- euler.y = Math::asin(-sy);
- euler.z = Math::atan2(elements[1][0], elements[0][0]);
- } else {
- // It's -1
- euler.x = 0;
- euler.y = Math_PI / 2.0;
- euler.z = -Math::atan2(elements[0][1], elements[1][1]);
- }
- } else {
- // It's 1
- euler.x = 0;
- euler.y = -Math_PI / 2.0;
- euler.z = -Math::atan2(elements[0][1], elements[1][1]);
- }
- return euler;
-}
-
-void Basis::set_euler_zyx(const Vector3 &p_euler) {
- real_t c, s;
-
- c = Math::cos(p_euler.x);
- s = Math::sin(p_euler.x);
- Basis xmat(1.0, 0.0, 0.0, 0.0, c, -s, 0.0, s, c);
-
- c = Math::cos(p_euler.y);
- s = Math::sin(p_euler.y);
- Basis ymat(c, 0.0, s, 0.0, 1.0, 0.0, -s, 0.0, c);
-
- c = Math::cos(p_euler.z);
- s = Math::sin(p_euler.z);
- Basis zmat(c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0);
-
- *this = zmat * ymat * xmat;
}
bool Basis::is_equal_approx(const Basis &p_basis) const {
diff --git a/core/math/basis.h b/core/math/basis.h
index eb107d7e4e..617d005f19 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -85,40 +85,35 @@ public:
void rotate(const Quaternion &p_quaternion);
Basis rotated(const Quaternion &p_quaternion) const;
- Vector3 get_rotation_euler() const;
+ enum EulerOrder {
+ EULER_ORDER_XYZ,
+ EULER_ORDER_XZY,
+ EULER_ORDER_YXZ,
+ EULER_ORDER_YZX,
+ EULER_ORDER_ZXY,
+ EULER_ORDER_ZYX
+ };
+
+ Vector3 get_euler_normalized(EulerOrder p_order = EULER_ORDER_YXZ) const;
void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
Quaternion get_rotation_quaternion() const;
- Vector3 get_rotation() const { return get_rotation_euler(); };
void rotate_to_align(Vector3 p_start_direction, Vector3 p_end_direction);
Vector3 rotref_posscale_decomposition(Basis &rotref) const;
- Vector3 get_euler_xyz() const;
- void set_euler_xyz(const Vector3 &p_euler);
-
- Vector3 get_euler_xzy() const;
- void set_euler_xzy(const Vector3 &p_euler);
-
- Vector3 get_euler_yzx() const;
- void set_euler_yzx(const Vector3 &p_euler);
-
- Vector3 get_euler_yxz() const;
- void set_euler_yxz(const Vector3 &p_euler);
-
- Vector3 get_euler_zxy() const;
- void set_euler_zxy(const Vector3 &p_euler);
-
- Vector3 get_euler_zyx() const;
- void set_euler_zyx(const Vector3 &p_euler);
+ Vector3 get_euler(EulerOrder p_order = EULER_ORDER_YXZ) const;
+ void set_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ);
+ static Basis from_euler(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ) {
+ Basis b;
+ b.set_euler(p_euler, p_order);
+ return b;
+ }
Quaternion get_quaternion() const;
void set_quaternion(const Quaternion &p_quaternion);
- Vector3 get_euler() const { return get_euler_yxz(); }
- void set_euler(const Vector3 &p_euler) { set_euler_yxz(p_euler); }
-
void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const;
void set_axis_angle(const Vector3 &p_axis, real_t p_phi);
@@ -250,9 +245,6 @@ public:
Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); };
Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); }
- Basis(const Vector3 &p_euler) { set_euler(p_euler); }
- Basis(const Vector3 &p_euler, const Vector3 &p_scale) { set_euler_scale(p_euler, p_scale); }
-
Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); }
Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); }
static Basis from_scale(const Vector3 &p_scale);
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index f67035c803..684814b1ae 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -594,8 +594,6 @@ private:
IntermediateHull() {
}
-
- void print();
};
enum Orientation { NONE,
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 8e5830f9b3..6010159597 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -37,8 +37,6 @@
#include "core/templates/vector.h"
class Geometry2D {
- Geometry2D();
-
public:
static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) {
Vector2 d1 = q1 - p1; // Direction vector of segment S1.
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index 766689e222..6a59b34585 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -36,8 +36,6 @@
#include "core/templates/vector.h"
class Geometry3D {
- Geometry3D();
-
public:
static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {
// Do the function 'd' as defined by pb. I think it's a dot product of some sort.
diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp
index 3f1d2c58e5..944474686a 100644
--- a/core/math/quaternion.cpp
+++ b/core/math/quaternion.cpp
@@ -44,7 +44,7 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const {
// This implementation uses XYZ convention (Z is the first rotation).
Vector3 Quaternion::get_euler_xyz() const {
Basis m(*this);
- return m.get_euler_xyz();
+ return m.get_euler(Basis::EULER_ORDER_XYZ);
}
// get_euler_yxz returns a vector containing the Euler angles in the format
@@ -56,7 +56,7 @@ Vector3 Quaternion::get_euler_yxz() const {
ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
#endif
Basis m(*this);
- return m.get_euler_yxz();
+ return m.get_euler(Basis::EULER_ORDER_YXZ);
}
void Quaternion::operator*=(const Quaternion &p_q) {
@@ -189,6 +189,15 @@ Quaternion::operator String() const {
return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ", " + String::num_real(w, false) + ")";
}
+Vector3 Quaternion::get_axis() const {
+ real_t r = ((real_t)1) / Math::sqrt(1 - w * w);
+ return Vector3(x * r, y * r, z * r);
+}
+
+float Quaternion::get_angle() const {
+ return 2 * Math::acos(w);
+}
+
Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
#ifdef MATH_CHECKS
ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index 35324323b3..e20ea74eb4 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -72,6 +72,9 @@ public:
Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const;
Quaternion cubic_slerp(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const;
+ Vector3 get_axis() const;
+ float get_angle() const;
+
_FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = 2 * Math::acos(w);
real_t r = ((real_t)1) / Math::sqrt(1 - w * w);
diff --git a/core/math/vector3.h b/core/math/vector3.h
index e65ac31c02..dc9aa60458 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -32,9 +32,9 @@
#define VECTOR3_H
#include "core/math/math_funcs.h"
+#include "core/math/vector2.h"
#include "core/math/vector3i.h"
#include "core/string/ustring.h"
-
class Basis;
struct Vector3 {
@@ -103,6 +103,31 @@ struct Vector3 {
Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
+ _FORCE_INLINE_ Vector2 octahedron_encode() const {
+ Vector3 n = *this;
+ n /= Math::abs(n.x) + Math::abs(n.y) + Math::abs(n.z);
+ Vector2 o;
+ if (n.z >= 0.0) {
+ o.x = n.x;
+ o.y = n.y;
+ } else {
+ o.x = (1.0 - Math::abs(n.y)) * (n.x >= 0.0 ? 1.0 : -1.0);
+ o.y = (1.0 - Math::abs(n.x)) * (n.y >= 0.0 ? 1.0 : -1.0);
+ }
+ o.x = o.x * 0.5 + 0.5;
+ o.y = o.y * 0.5 + 0.5;
+ return o;
+ }
+
+ static _FORCE_INLINE_ Vector3 octahedron_decode(const Vector2 &p_oct) {
+ Vector2 f(p_oct.x * 2.0 - 1.0, p_oct.y * 2.0 - 1.0);
+ Vector3 n(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y));
+ float t = CLAMP(-n.z, 0.0, 1.0);
+ n.x += n.x >= 0 ? -t : t;
+ n.y += n.y >= 0 ? -t : t;
+ return n.normalized();
+ }
+
_FORCE_INLINE_ Vector3 cross(const Vector3 &p_b) const;
_FORCE_INLINE_ real_t dot(const Vector3 &p_b) const;
Basis outer(const Vector3 &p_b) const;
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 9c84c2add7..07006e7968 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -76,7 +76,7 @@ bool UndoRedo::_redo(bool p_execute) {
}
void UndoRedo::create_action(const String &p_name, MergeMode p_mode) {
- uint32_t ticks = OS::get_singleton()->get_ticks_msec();
+ uint64_t ticks = OS::get_singleton()->get_ticks_msec();
if (action_level == 0) {
_discard_redo();
diff --git a/core/os/memory.h b/core/os/memory.h
index f67384a17e..ac56a12330 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -42,7 +42,6 @@
#endif
class Memory {
- Memory();
#ifdef DEBUG_ENABLED
static SafeNumeric<uint64_t> mem_usage;
static SafeNumeric<uint64_t> max_usage;
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 7404ffdcd5..5892f91ff3 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -431,6 +431,15 @@ bool OS::has_feature(const String &p_feature) {
if (p_feature == "arm") {
return true;
}
+#elif defined(__riscv)
+#if __riscv_xlen == 8
+ if (p_feature == "rv64") {
+ return true;
+ }
+#endif
+ if (p_feature == "riscv") {
+ return true;
+ }
#endif
if (_check_internal_feature_support(p_feature)) {
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index daeb7fbd17..397743fb6e 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -2137,7 +2137,7 @@ int64_t String::hex_to_int() const {
s++;
}
- if (len > 2 && s[0] == '0' && s[1] == 'x') {
+ if (len > 2 && s[0] == '0' && lower_case(s[1]) == 'x') {
s += 2;
}
@@ -2151,7 +2151,7 @@ int64_t String::hex_to_int() const {
} else if (c >= 'a' && c <= 'f') {
n = (c - 'a') + 10;
} else {
- return 0;
+ ERR_FAIL_COND_V_MSG(true, 0, "Invalid hexadecimal notation character \"" + chr(*s) + "\" in string \"" + *this + "\".");
}
// Check for overflow/underflow, with special case to ensure INT64_MIN does not result in error
bool overflow = ((hex > INT64_MAX / 16) && (sign == 1 || (sign == -1 && hex != (INT64_MAX >> 4) + 1))) || (sign == -1 && hex == (INT64_MAX >> 4) + 1 && c > '0');
@@ -2178,7 +2178,7 @@ int64_t String::bin_to_int() const {
s++;
}
- if (len > 2 && s[0] == '0' && s[1] == 'b') {
+ if (len > 2 && s[0] == '0' && lower_case(s[1]) == 'b') {
s += 2;
}
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 8592a1dc62..f06d767cf5 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -88,6 +88,7 @@ struct VariantCaster<const T &> {
VARIANT_ENUM_CAST(Object::ConnectFlags);
VARIANT_ENUM_CAST(Vector3::Axis);
+VARIANT_ENUM_CAST(Basis::EulerOrder);
VARIANT_ENUM_CAST(Error);
VARIANT_ENUM_CAST(Side);
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 3214fc125d..81428caca1 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -313,7 +313,6 @@ bool Variant::can_convert(Variant::Type p_type_from, Variant::Type p_type_to) {
case BASIS: {
static const Type valid[] = {
QUATERNION,
- VECTOR3,
NIL
};
@@ -620,7 +619,6 @@ bool Variant::can_convert_strict(Variant::Type p_type_from, Variant::Type p_type
case BASIS: {
static const Type valid[] = {
QUATERNION,
- VECTOR3,
NIL
};
@@ -1889,8 +1887,6 @@ Variant::operator Basis() const {
return *_data._basis;
} else if (type == QUATERNION) {
return *reinterpret_cast<const Quaternion *>(_data._mem);
- } else if (type == VECTOR3) {
- return Basis(*reinterpret_cast<const Vector3 *>(_data._mem));
} else if (type == TRANSFORM3D) { // unexposed in Variant::can_convert?
return _data._transform3d->basis;
} else {
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 6284caae2d..ec3a6a5ca8 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1581,6 +1581,8 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, bounce, sarray("n"), varray());
bind_method(Vector3, reflect, sarray("n"), varray());
bind_method(Vector3, sign, sarray(), varray());
+ bind_method(Vector3, octahedron_encode, sarray(), varray());
+ bind_static_method(Vector3, octahedron_decode, sarray("uv"), varray());
/* Vector3i */
@@ -1617,6 +1619,8 @@ static void _register_variant_builtin_methods() {
bind_method(Quaternion, slerpni, sarray("to", "weight"), varray());
bind_method(Quaternion, cubic_slerp, sarray("b", "pre_a", "post_b", "weight"), varray());
bind_method(Quaternion, get_euler, sarray(), varray());
+ bind_method(Quaternion, get_axis, sarray(), varray());
+ bind_method(Quaternion, get_angle, sarray(), varray());
/* Color */
@@ -1727,7 +1731,7 @@ static void _register_variant_builtin_methods() {
bind_methodv(Basis, rotated, static_cast<Basis (Basis::*)(const Vector3 &, real_t) const>(&Basis::rotated), sarray("axis", "phi"), varray());
bind_method(Basis, scaled, sarray("scale"), varray());
bind_method(Basis, get_scale, sarray(), varray());
- bind_method(Basis, get_euler, sarray(), varray());
+ bind_method(Basis, get_euler, sarray("order"), varray(Basis::EULER_ORDER_YXZ));
bind_method(Basis, tdotx, sarray("with"), varray());
bind_method(Basis, tdoty, sarray("with"), varray());
bind_method(Basis, tdotz, sarray("with"), varray());
@@ -1737,6 +1741,7 @@ static void _register_variant_builtin_methods() {
bind_method(Basis, get_rotation_quaternion, sarray(), varray());
bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0)));
bind_static_method(Basis, from_scale, sarray("scale"), varray());
+ bind_static_method(Basis, from_euler, sarray("euler", "order"), varray(Basis::EULER_ORDER_YXZ));
/* AABB */
@@ -2105,6 +2110,13 @@ static void _register_variant_builtin_methods() {
_VariantCall::add_variant_constant(Variant::VECTOR2I, "UP", Vector2i(0, -1));
_VariantCall::add_variant_constant(Variant::VECTOR2I, "DOWN", Vector2i(0, 1));
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XYZ", Basis::EULER_ORDER_XYZ);
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_XZY", Basis::EULER_ORDER_XZY);
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YXZ", Basis::EULER_ORDER_YXZ);
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_YZX", Basis::EULER_ORDER_YZX);
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZXY", Basis::EULER_ORDER_ZXY);
+ _VariantCall::add_constant(Variant::BASIS, "EULER_ORDER_ZYX", Basis::EULER_ORDER_ZYX);
+
_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "IDENTITY", Transform2D());
_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_X", Transform2D(-1, 0, 0, 1, 0, 0));
_VariantCall::add_variant_constant(Variant::TRANSFORM2D, "FLIP_Y", Transform2D(1, 0, 0, -1, 0, 0));
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index 6aba7d7d58..5c14f30180 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -128,10 +128,10 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructNoArgs<Quaternion>>(sarray());
add_constructor<VariantConstructor<Quaternion, Quaternion>>(sarray("from"));
add_constructor<VariantConstructor<Quaternion, Basis>>(sarray("from"));
- add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler"));
add_constructor<VariantConstructor<Quaternion, Vector3, double>>(sarray("axis", "angle"));
add_constructor<VariantConstructor<Quaternion, Vector3, Vector3>>(sarray("arc_from", "arc_to"));
add_constructor<VariantConstructor<Quaternion, double, double, double, double>>(sarray("x", "y", "z", "w"));
+ add_constructor<VariantConstructor<Quaternion, Vector3>>(sarray("euler_yxz"));
add_constructor<VariantConstructNoArgs<::AABB>>(sarray());
add_constructor<VariantConstructor<::AABB, ::AABB>>(sarray("from"));
@@ -140,7 +140,6 @@ void Variant::_register_variant_constructors() {
add_constructor<VariantConstructNoArgs<Basis>>(sarray());
add_constructor<VariantConstructor<Basis, Basis>>(sarray("from"));
add_constructor<VariantConstructor<Basis, Quaternion>>(sarray("from"));
- add_constructor<VariantConstructor<Basis, Vector3>>(sarray("euler"));
add_constructor<VariantConstructor<Basis, Vector3, double>>(sarray("axis", "phi"));
add_constructor<VariantConstructor<Basis, Vector3, Vector3, Vector3>>(sarray("x_axis", "y_axis", "z_axis"));
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 37383ff2ec..2ba24b5af8 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -757,6 +757,12 @@ VARIANT_ACCESSOR_NUMBER(Error)
VARIANT_ACCESSOR_NUMBER(Side)
template <>
+struct VariantInternalAccessor<Basis::EulerOrder> {
+ static _FORCE_INLINE_ Basis::EulerOrder get(const Variant *v) { return Basis::EulerOrder(*VariantInternal::get_int(v)); }
+ static _FORCE_INLINE_ void set(Variant *v, Basis::EulerOrder p_value) { *VariantInternal::get_int(v) = p_value; }
+};
+
+template <>
struct VariantInternalAccessor<ObjectID> {
static _FORCE_INLINE_ ObjectID get(const Variant *v) { return ObjectID(*VariantInternal::get_int(v)); }
static _FORCE_INLINE_ void set(Variant *v, ObjectID p_value) { *VariantInternal::get_int(v) = p_value; }
diff --git a/doc/Makefile b/doc/Makefile
index d4bc53bcf9..c46a485da1 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -17,7 +17,7 @@ doxygen:
rst:
rm -rf $(OUTPUTDIR)/rst
mkdir -p $(OUTPUTDIR)/rst
- python3 $(TOOLSDIR)/makerst.py -o $(OUTPUTDIR)/rst $(CLASSES)
+ python3 $(TOOLSDIR)/make_rst.py -o $(OUTPUTDIR)/rst $(CLASSES)
rstjs:
rm -rf $(OUTPUTDIR)/rstjs
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index ebcced7dc4..e3bb60f6de 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -215,6 +215,14 @@
Clear the animation (clear all tracks and reset all).
</description>
</method>
+ <method name="compress">
+ <return type="void" />
+ <argument index="0" name="page_size" type="int" default="8192" />
+ <argument index="1" name="fps" type="int" default="120" />
+ <argument index="2" name="split_tolerance" type="float" default="4.0" />
+ <description>
+ </description>
+ </method>
<method name="copy_track">
<return type="void" />
<argument index="0" name="track_idx" type="int" />
@@ -226,6 +234,7 @@
<method name="find_track" qualifiers="const">
<return type="int" />
<argument index="0" name="path" type="NodePath" />
+ <argument index="1" name="type" type="int" enum="Animation.TrackType" />
<description>
Returns the index of the specified track. If the track is not found, return -1.
</description>
@@ -370,6 +379,12 @@
Insert a generic key in a given track.
</description>
</method>
+ <method name="track_is_compressed" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="track_idx" type="int" />
+ <description>
+ </description>
+ </method>
<method name="track_is_enabled" qualifiers="const">
<return type="bool" />
<argument index="0" name="track_idx" type="int" />
diff --git a/doc/classes/Area2D.xml b/doc/classes/Area2D.xml
index ddfc3b1869..c6a3f87042 100644
--- a/doc/classes/Area2D.xml
+++ b/doc/classes/Area2D.xml
@@ -16,13 +16,15 @@
<method name="get_overlapping_areas" qualifiers="const">
<return type="Area2D[]" />
<description>
- Returns a list of intersecting [Area2D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
+ Returns a list of intersecting [Area2D]s. The overlapping area's [member CollisionObject2D.collision_layer] must be part of this area's [member CollisionObject2D.collision_mask] in order to be detected.
+ For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
</description>
</method>
<method name="get_overlapping_bodies" qualifiers="const">
<return type="Node2D[]" />
<description>
- Returns a list of intersecting [PhysicsBody2D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
+ Returns a list of intersecting [PhysicsBody2D]s. The overlapping body's [member CollisionObject2D.collision_layer] must be part of this area's [member CollisionObject2D.collision_mask] in order to be detected.
+ For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
</description>
</method>
<method name="overlaps_area" qualifiers="const">
diff --git a/doc/classes/Area3D.xml b/doc/classes/Area3D.xml
index 896bfcd14e..14225c52a4 100644
--- a/doc/classes/Area3D.xml
+++ b/doc/classes/Area3D.xml
@@ -14,13 +14,15 @@
<method name="get_overlapping_areas" qualifiers="const">
<return type="Area3D[]" />
<description>
- Returns a list of intersecting [Area3D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
+ Returns a list of intersecting [Area3D]s. The overlapping area's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
+ For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
</description>
</method>
<method name="get_overlapping_bodies" qualifiers="const">
<return type="Node3D[]" />
<description>
- Returns a list of intersecting [PhysicsBody3D]s. For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
+ Returns a list of intersecting [PhysicsBody3D]s. The overlapping body's [member CollisionObject3D.collision_layer] must be part of this area's [member CollisionObject3D.collision_mask] in order to be detected.
+ For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.
</description>
</method>
<method name="overlaps_area" qualifiers="const">
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 7f0d4cbbe3..6215c658c3 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -42,14 +42,6 @@
</method>
<method name="Basis" qualifiers="constructor">
<return type="Basis" />
- <argument index="0" name="euler" type="Vector3" />
- <description>
- Constructs a pure rotation basis matrix from the given Euler angles (in the YXZ convention: when *composing*, first Y, then X, and Z last), given in the vector format as (X angle, Y angle, Z angle).
- Consider using the [Quaternion] constructor instead, which uses a quaternion instead of Euler angles.
- </description>
- </method>
- <method name="Basis" qualifiers="constructor">
- <return type="Basis" />
<argument index="0" name="from" type="Quaternion" />
<description>
Constructs a pure rotation basis matrix from the given quaternion.
@@ -71,6 +63,13 @@
A negative determinant means the basis has a negative scale. A zero determinant means the basis isn't invertible, and is usually considered invalid.
</description>
</method>
+ <method name="from_euler" qualifiers="static">
+ <return type="Basis" />
+ <argument index="0" name="euler" type="Vector3" />
+ <argument index="1" name="order" type="int" default="2" />
+ <description>
+ </description>
+ </method>
<method name="from_scale" qualifiers="static">
<return type="Basis" />
<argument index="0" name="scale" type="Vector3" />
@@ -80,6 +79,7 @@
</method>
<method name="get_euler" qualifiers="const">
<return type="Vector3" />
+ <argument index="0" name="order" type="int" default="2" />
<description>
Returns the basis's rotation in the form of Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last). The returned vector contains the rotation angles in the format (X angle, Y angle, Z angle).
Consider using the [method get_rotation_quaternion] method instead, which returns a [Quaternion] quaternion instead of Euler angles.
@@ -248,6 +248,18 @@
</member>
</members>
<constants>
+ <constant name="EULER_ORDER_XYZ" value="0">
+ </constant>
+ <constant name="EULER_ORDER_XZY" value="1">
+ </constant>
+ <constant name="EULER_ORDER_YXZ" value="2">
+ </constant>
+ <constant name="EULER_ORDER_YZX" value="3">
+ </constant>
+ <constant name="EULER_ORDER_ZXY" value="4">
+ </constant>
+ <constant name="EULER_ORDER_ZYX" value="5">
+ </constant>
<constant name="IDENTITY" value="Basis(1, 0, 0, 0, 1, 0, 0, 0, 1)">
The identity basis, with no rotation or scaling applied.
This is identical to calling [code]Basis()[/code] without any parameters. This constant can be used to make your code clearer, and for consistency with C#.
diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml
index dfbd6c0680..ba55fda59c 100644
--- a/doc/classes/Button.xml
+++ b/doc/classes/Button.xml
@@ -119,6 +119,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
Text [Color] used when the [Button] is disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ Text [Color] used when the [Button] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
Text [Color] used when the [Button] is being hovered.
</theme_item>
@@ -143,6 +146,9 @@
<theme_item name="icon_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Icon modulate [Color] used when the [Button] is disabled.
</theme_item>
+ <theme_item name="icon_focus_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
+ Icon modulate [Color] used when the [Button] is focused. Only replaces the normal modulate color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="icon_hover_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Icon modulate [Color] used when the [Button] is being hovered.
</theme_item>
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index 738594ea5d..7be18c1d5c 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -53,7 +53,7 @@
<argument index="1" name="pos" type="Vector2" />
<argument index="2" name="char" type="String" />
<argument index="3" name="next" type="String" default="&quot;&quot;" />
- <argument index="4" name="size" type="int" default="-1" />
+ <argument index="4" name="size" type="int" default="16" />
<argument index="5" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="6" name="outline_size" type="int" default="0" />
<argument index="7" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
@@ -146,7 +146,7 @@
<argument index="3" name="align" type="int" enum="HAlign" default="0" />
<argument index="4" name="width" type="float" default="-1" />
<argument index="5" name="max_lines" type="int" default="-1" />
- <argument index="6" name="size" type="int" default="-1" />
+ <argument index="6" name="size" type="int" default="16" />
<argument index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="8" name="outline_size" type="int" default="0" />
<argument index="9" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
@@ -238,7 +238,7 @@
<argument index="2" name="text" type="String" />
<argument index="3" name="align" type="int" enum="HAlign" default="0" />
<argument index="4" name="width" type="float" default="-1" />
- <argument index="5" name="size" type="int" default="-1" />
+ <argument index="5" name="size" type="int" default="16" />
<argument index="6" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="7" name="outline_size" type="int" default="0" />
<argument index="8" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml
index a674fa8339..18f668a052 100644
--- a/doc/classes/CheckBox.xml
+++ b/doc/classes/CheckBox.xml
@@ -21,6 +21,7 @@
The check icon to display when the [CheckBox] is checked.
</theme_item>
<theme_item name="checked_disabled" data_type="icon" type="Texture2D">
+ The check icon to display when the [CheckBox] is checked and disabled.
</theme_item>
<theme_item name="disabled" data_type="style" type="StyleBox">
The [StyleBox] to display as a background when the [CheckBox] is disabled.
@@ -37,6 +38,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
The [CheckBox] text's font color when it's disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ The [CheckBox] text's font color when it's focused. Only replaces the normal text color of the checkbox. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
The [CheckBox] text's font color when it's hovered.
</theme_item>
@@ -84,6 +88,7 @@
The check icon to display when the [CheckBox] is unchecked.
</theme_item>
<theme_item name="unchecked_disabled" data_type="icon" type="Texture2D">
+ The check icon to display when the [CheckBox] is unchecked and disabled.
</theme_item>
</theme_items>
</class>
diff --git a/doc/classes/CheckButton.xml b/doc/classes/CheckButton.xml
index 12bd493ae1..c86fadb6b0 100644
--- a/doc/classes/CheckButton.xml
+++ b/doc/classes/CheckButton.xml
@@ -32,6 +32,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
The [CheckButton] text's font color when it's disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ The [CheckButton] text's font color when it's focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
The [CheckButton] text's font color when it's hovered.
</theme_item>
diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index 58b6e89507..fff27d2d56 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -74,6 +74,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.3)">
Text [Color] used when the [ColorPickerButton] is disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
+ Text [Color] used when the [ColorPickerButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)">
Text [Color] used when the [ColorPickerButton] is being hovered.
</theme_item>
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 332d171457..63e3eb7a5f 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1044,7 +1044,7 @@
<member name="rect_scale" type="Vector2" setter="set_scale" getter="get_scale" default="Vector2(1, 1)">
The node's scale, relative to its [member rect_size]. Change this property to scale the node around its [member rect_pivot_offset]. The Control's [member hint_tooltip] will also scale according to this value.
[b]Note:[/b] This property is mainly intended to be used for animation purposes. Text inside the Control will look pixelated or blurry when the Control is scaled. To support multiple resolutions in your project, use an appropriate viewport stretch mode as described in the [url=https://docs.godotengine.org/en/latest/tutorials/viewports/multiple_resolutions.html]documentation[/url] instead of scaling Controls individually.
- [b]Note:[/b] If the Control node is a child of a [Container] node, the scale will be reset to [code]Vector2(1, 1)[/code] when the scene is instantiated. To set the Control's scale when it's instantiated, wait for one frame using [code]yield(get_tree(), "process_frame")[/code] then set its [member rect_scale] property.
+ [b]Note:[/b] If the Control node is a child of a [Container] node, the scale will be reset to [code]Vector2(1, 1)[/code] when the scene is instantiated. To set the Control's scale when it's instantiated, wait for one frame using [code]await get_tree().process_frame[/code] then set its [member rect_scale] property.
</member>
<member name="rect_size" type="Vector2" setter="_set_size" getter="get_size" default="Vector2(0, 0)">
The size of the node's bounding rectangle, in pixels. [Container] nodes update this property automatically.
diff --git a/doc/classes/DirectionalLight2D.xml b/doc/classes/DirectionalLight2D.xml
index 317cf6e66c..1e5c9bc09a 100644
--- a/doc/classes/DirectionalLight2D.xml
+++ b/doc/classes/DirectionalLight2D.xml
@@ -8,7 +8,7 @@
</tutorials>
<members>
<member name="height" type="float" setter="set_height" getter="get_height" default="0.0">
- The height of the light. Used with 2D normal mapping.
+ The height of the light. Used with 2D normal mapping. Ranges from 0 (parallel to the plane) to 1 (perpendicular to the plane).
</member>
<member name="max_distance" type="float" setter="set_max_distance" getter="get_max_distance" default="10000.0">
</member>
diff --git a/doc/classes/DirectionalLight3D.xml b/doc/classes/DirectionalLight3D.xml
index f2a6e5b6f8..44d982cbcc 100644
--- a/doc/classes/DirectionalLight3D.xml
+++ b/doc/classes/DirectionalLight3D.xml
@@ -11,13 +11,13 @@
</tutorials>
<members>
<member name="directional_shadow_blend_splits" type="bool" setter="set_blend_splits" getter="is_blend_splits_enabled" default="false">
- If [code]true[/code], shadow detail is sacrificed in exchange for smoother transitions between splits. This is ignored when [member directional_shadow_mode] is [code]SHADOW_ORTHOGONAL[/code].
+ If [code]true[/code], shadow detail is sacrificed in exchange for smoother transitions between splits. Enabling shadow blend splitting also has a moderate performance cost. This is ignored when [member directional_shadow_mode] is [constant SHADOW_ORTHOGONAL].
</member>
<member name="directional_shadow_fade_start" type="float" setter="set_param" getter="get_param" default="0.8">
- Proportion of [member directional_shadow_max_distance] at which point the shadow starts to fade. At [member directional_shadow_max_distance] the shadow will disappear.
+ Proportion of [member directional_shadow_max_distance] at which point the shadow starts to fade. At [member directional_shadow_max_distance], the shadow will disappear. The default value is a balance between smooth fading and distant shadow visibility. If the camera moves fast and the [member directional_shadow_max_distance] is low, consider lowering [member directional_shadow_fade_start] below [code]0.8[/code] to make shadow transitions less noticeable. On the other hand, if you tuned [member directional_shadow_max_distance] to cover the entire scene, you can set [member directional_shadow_fade_start] to [code]1.0[/code] to prevent the shadow from fading in the distance (it will suddenly cut off instead).
</member>
<member name="directional_shadow_max_distance" type="float" setter="set_param" getter="get_param" default="100.0">
- The maximum distance for shadow splits.
+ The maximum distance for shadow splits. Increasing this value will make directional shadows visible from further away, at the cost of lower overall shadow detail and performance (since more objects need to be included in the directional shadow rendering).
</member>
<member name="directional_shadow_mode" type="int" setter="set_shadow_mode" getter="get_shadow_mode" enum="DirectionalLight3D.ShadowMode" default="2">
The light's shadow rendering algorithm. See [enum ShadowMode].
@@ -26,13 +26,13 @@
Sets the size of the directional shadow pancake. The pancake offsets the start of the shadow's camera frustum to provide a higher effective depth resolution for the shadow. However, a high pancake size can cause artifacts in the shadows of large objects that are close to the edge of the frustum. Reducing the pancake size can help. Setting the size to [code]0[/code] turns off the pancaking effect.
</member>
<member name="directional_shadow_split_1" type="float" setter="set_param" getter="get_param" default="0.1">
- The distance from camera to shadow split 1. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [code]SHADOW_PARALLEL_2_SPLITS[/code] or [code]SHADOW_PARALLEL_4_SPLITS[/code].
+ The distance from camera to shadow split 1. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [constant SHADOW_PARALLEL_2_SPLITS] or [constant SHADOW_PARALLEL_4_SPLITS].
</member>
<member name="directional_shadow_split_2" type="float" setter="set_param" getter="get_param" default="0.2">
- The distance from shadow split 1 to split 2. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [code]SHADOW_PARALLEL_4_SPLITS[/code].
+ The distance from shadow split 1 to split 2. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [constant SHADOW_PARALLEL_4_SPLITS].
</member>
<member name="directional_shadow_split_3" type="float" setter="set_param" getter="get_param" default="0.5">
- The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [code]SHADOW_PARALLEL_4_SPLITS[/code].
+ The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used when [member directional_shadow_mode] is [constant SHADOW_PARALLEL_4_SPLITS].
</member>
<member name="shadow_bias" type="float" setter="set_param" getter="get_param" override="true" default="0.1" />
<member name="use_in_sky_only" type="bool" setter="set_sky_only" getter="is_sky_only" default="false">
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 4b43286594..92d6a220d2 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -819,6 +819,8 @@
</constant>
<constant name="FEATURE_SWAP_BUFFERS" value="17" enum="Feature">
</constant>
+ <constant name="FEATURE_CLIPBOARD_PRIMARY" value="19" enum="Feature">
+ </constant>
<constant name="MOUSE_MODE_VISIBLE" value="0" enum="MouseMode">
Makes the mouse cursor visible if it is hidden.
</constant>
diff --git a/doc/classes/EditorImportPlugin.xml b/doc/classes/EditorImportPlugin.xml
index 6011a5411d..cb33f159ef 100644
--- a/doc/classes/EditorImportPlugin.xml
+++ b/doc/classes/EditorImportPlugin.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
[EditorImportPlugin]s provide a way to extend the editor's resource import functionality. Use them to import resources from custom files or to provide alternatives to the editor's existing importers.
- EditorImportPlugins work by associating with specific file extensions and a resource type. See [method _get_recognized_extensions] and [method _get_resource_type]. They may optionally specify some import presets that affect the import process. EditorImportPlugins are responsible for creating the resources and saving them in the [code].godot/imported[/code] directory (see [member ProjectSettings.application/config/project_data_dir_name]).
+ EditorImportPlugins work by associating with specific file extensions and a resource type. See [method _get_recognized_extensions] and [method _get_resource_type]. They may optionally specify some import presets that affect the import process. EditorImportPlugins are responsible for creating the resources and saving them in the [code].godot/imported[/code] directory (see [member ProjectSettings.application/config/use_hidden_project_data_directory]).
Below is an example EditorImportPlugin that imports a [Mesh] from a file with the extension ".special" or ".spec":
[codeblocks]
[gdscript]
@@ -198,7 +198,7 @@
<method name="_get_save_extension" qualifiers="virtual const">
<return type="String" />
<description>
- Gets the extension used to save this resource in the [code].godot/imported[/code] directory (see [member ProjectSettings.application/config/project_data_dir_name]).
+ Gets the extension used to save this resource in the [code].godot/imported[/code] directory (see [member ProjectSettings.application/config/use_hidden_project_data_directory]).
</description>
</method>
<method name="_get_visible_name" qualifiers="virtual const">
diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml
index 39881e9eb7..01bc2837f1 100644
--- a/doc/classes/Font.xml
+++ b/doc/classes/Font.xml
@@ -82,7 +82,7 @@
<argument index="1" name="pos" type="Vector2" />
<argument index="2" name="char" type="int" />
<argument index="3" name="next" type="int" default="0" />
- <argument index="4" name="size" type="int" default="-1" />
+ <argument index="4" name="size" type="int" default="16" />
<argument index="5" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="6" name="outline_size" type="int" default="0" />
<argument index="7" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
@@ -99,7 +99,7 @@
<argument index="3" name="align" type="int" enum="HAlign" default="0" />
<argument index="4" name="width" type="float" default="-1" />
<argument index="5" name="max_lines" type="int" default="-1" />
- <argument index="6" name="size" type="int" default="-1" />
+ <argument index="6" name="size" type="int" default="16" />
<argument index="7" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="8" name="outline_size" type="int" default="0" />
<argument index="9" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
@@ -116,7 +116,7 @@
<argument index="2" name="text" type="String" />
<argument index="3" name="align" type="int" enum="HAlign" default="0" />
<argument index="4" name="width" type="float" default="-1" />
- <argument index="5" name="size" type="int" default="-1" />
+ <argument index="5" name="size" type="int" default="16" />
<argument index="6" name="modulate" type="Color" default="Color(1, 1, 1, 1)" />
<argument index="7" name="outline_size" type="int" default="0" />
<argument index="8" name="outline_modulate" type="Color" default="Color(1, 1, 1, 0)" />
@@ -128,7 +128,7 @@
</method>
<method name="get_ascent" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" default="-1" />
+ <argument index="0" name="size" type="int" default="16" />
<description>
Returns the average font ascent (number of pixels above the baseline).
[b]Note:[/b] Real ascent of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate (e.g. as the ascent of empty line).
@@ -138,7 +138,7 @@
<return type="Vector2" />
<argument index="0" name="char" type="int" />
<argument index="1" name="next" type="int" default="0" />
- <argument index="2" name="size" type="int" default="-1" />
+ <argument index="2" name="size" type="int" default="16" />
<description>
Returns the size of a character, optionally taking kerning into account if the next character is provided.
[b]Note:[/b] Do not use this function to calculate width of the string character by character, use [method get_string_size] or [TextLine] instead. The height returned is the font height (see also [method get_height]) and has no relation to the glyph height.
@@ -166,7 +166,7 @@
</method>
<method name="get_descent" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" default="-1" />
+ <argument index="0" name="size" type="int" default="16" />
<description>
Returns the average font descent (number of pixels below the baseline).
[b]Note:[/b] Real descent of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate (e.g. as the descent of empty line).
@@ -174,7 +174,7 @@
</method>
<method name="get_height" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" default="-1" />
+ <argument index="0" name="size" type="int" default="16" />
<description>
Returns the total average font height (ascent plus descent) in pixels.
[b]Note:[/b] Real height of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate (e.g. as the height of empty line).
@@ -184,7 +184,7 @@
<return type="Vector2" />
<argument index="0" name="text" type="String" />
<argument index="1" name="width" type="float" default="-1" />
- <argument index="2" name="size" type="int" default="-1" />
+ <argument index="2" name="size" type="int" default="16" />
<argument index="3" name="flags" type="int" default="96" />
<description>
Returns the size of a bounding box of a string broken into the lines, taking kerning and advance into account.
@@ -201,7 +201,7 @@
<method name="get_string_size" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="text" type="String" />
- <argument index="1" name="size" type="int" default="-1" />
+ <argument index="1" name="size" type="int" default="16" />
<argument index="2" name="align" type="int" enum="HAlign" default="0" />
<argument index="3" name="width" type="float" default="-1" />
<argument index="4" name="flags" type="int" default="3" />
@@ -220,7 +220,7 @@
</method>
<method name="get_underline_position" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" default="-1" />
+ <argument index="0" name="size" type="int" default="16" />
<description>
Return average pixel offset of the underline below the baseline.
[b]Note:[/b] Real underline position of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate.
@@ -228,7 +228,7 @@
</method>
<method name="get_underline_thickness" qualifiers="const">
<return type="float" />
- <argument index="0" name="size" type="int" default="-1" />
+ <argument index="0" name="size" type="int" default="16" />
<description>
Return average thickness of the underline.
[b]Note:[/b] Real underline thickness of the string is context-dependent and can be significantly different from the value returned by this function. Use it only as rough estimate.
@@ -272,9 +272,6 @@
</method>
</methods>
<members>
- <member name="base_size" type="int" setter="set_base_size" getter="get_base_size" default="16">
- Default font size.
- </member>
<member name="spacing_bottom" type="int" setter="set_spacing" getter="get_spacing" default="0">
Extra spacing at the bottom of the line in pixels.
</member>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index 88e4a67615..1fded42db2 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -37,6 +37,13 @@
Adds a new mapping entry (in SDL2 format) to the mapping database. Optionally update already connected devices.
</description>
</method>
+ <method name="flush_buffered_events">
+ <return type="void" />
+ <description>
+ Sends all input events which are in the current buffer to the game loop. These events may have been buffered as a result of accumulated input ([method set_use_accumulated_input]) or agile input flushing ([member ProjectSettings.input_devices/buffering/agile_event_flushing]).
+ The engine will already do this itself at key execution points (at least once per frame). However, this can be useful in advanced cases where you want precise control over the timing of event handling.
+ </description>
+ </method>
<method name="get_accelerometer" qualifiers="const">
<return type="Vector3" />
<description>
diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml
index e4445e9076..0329ed833f 100644
--- a/doc/classes/LinkButton.xml
+++ b/doc/classes/LinkButton.xml
@@ -75,6 +75,9 @@
<theme_item name="font_color" data_type="color" type="Color" default="Color(0.88, 0.88, 0.88, 1)">
Default text [Color] of the [LinkButton].
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ Text [Color] used when the [LinkButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
Text [Color] used when the [LinkButton] is being hovered.
</theme_item>
diff --git a/doc/classes/MenuButton.xml b/doc/classes/MenuButton.xml
index dda9b98db4..41ba1fcd0f 100644
--- a/doc/classes/MenuButton.xml
+++ b/doc/classes/MenuButton.xml
@@ -58,6 +58,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(1, 1, 1, 0.3)">
Text [Color] used when the [MenuButton] is disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ Text [Color] used when the [MenuButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
Text [Color] used when the [MenuButton] is being hovered.
</theme_item>
diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml
index 28acd77a39..2ca664e2b5 100644
--- a/doc/classes/Node3D.xml
+++ b/doc/classes/Node3D.xml
@@ -264,16 +264,28 @@
</method>
</methods>
<members>
+ <member name="basis" type="Basis" setter="set_basis" getter="get_basis">
+ Direct access to the 3x3 basis of the [Transform3D] property.
+ </member>
<member name="global_transform" type="Transform3D" setter="set_global_transform" getter="get_global_transform">
World3D space (global) [Transform3D] of this node.
</member>
<member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
Local position or translation of this node relative to the parent. This is equivalent to [code]transform.origin[/code].
</member>
+ <member name="quaternion" type="Quaternion" setter="set_quaternion" getter="get_quaternion">
+ Access to the node rotation as a [Quaternion]. This property is ideal for tweening complex rotations.
+ </member>
<member name="rotation" type="Vector3" setter="set_rotation" getter="get_rotation" default="Vector3(0, 0, 0)">
- Rotation part of the local transformation in radians, specified in terms of YXZ-Euler angles in the format (X angle, Y angle, Z angle).
+ Rotation part of the local transformation in radians, specified in terms of Euler angles. The angles construct a rotaton in the order specified by the [member rotation_order] property.
[b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful.
</member>
+ <member name="rotation_edit_mode" type="int" setter="set_rotation_edit_mode" getter="get_rotation_edit_mode" enum="Node3D.RotationEditMode" default="0">
+ Specify how rotation (and scale) will be presented in the editor.
+ </member>
+ <member name="rotation_order" type="int" setter="set_rotation_order" getter="get_rotation_order" enum="Node3D.RotationOrder" default="2">
+ Specify the axis rotation order of the [member rotation] property. The final orientation is constructed by rotating the Euler angles in the order specified by this property.
+ </member>
<member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)">
Scale part of the local transformation.
</member>
@@ -311,5 +323,23 @@
<constant name="NOTIFICATION_VISIBILITY_CHANGED" value="43">
Node3D nodes receives this notification when their visibility changes.
</constant>
+ <constant name="ROTATION_EDIT_MODE_EULER" value="0" enum="RotationEditMode">
+ </constant>
+ <constant name="ROTATION_EDIT_MODE_QUATERNION" value="1" enum="RotationEditMode">
+ </constant>
+ <constant name="ROTATION_EDIT_MODE_BASIS" value="2" enum="RotationEditMode">
+ </constant>
+ <constant name="ROTATION_ORDER_XYZ" value="0" enum="RotationOrder">
+ </constant>
+ <constant name="ROTATION_ORDER_XZY" value="1" enum="RotationOrder">
+ </constant>
+ <constant name="ROTATION_ORDER_YXZ" value="2" enum="RotationOrder">
+ </constant>
+ <constant name="ROTATION_ORDER_YZX" value="3" enum="RotationOrder">
+ </constant>
+ <constant name="ROTATION_ORDER_ZXY" value="4" enum="RotationOrder">
+ </constant>
+ <constant name="ROTATION_ORDER_ZYX" value="5" enum="RotationOrder">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 656fa61af7..15cc7b9b65 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -57,7 +57,7 @@
<argument index="0" name="msec" type="int" />
<description>
Delays execution of the current thread by [code]msec[/code] milliseconds. [code]msec[/code] must be greater than or equal to [code]0[/code]. Otherwise, [method delay_msec] will do nothing and will print an error message.
- [b]Note:[/b] [method delay_msec] is a [i]blocking[/i] way to delay code execution. To delay code execution in a non-blocking way, see [method SceneTree.create_timer]. Yielding with [method SceneTree.create_timer] will delay the execution of code placed below the [code]yield[/code] without affecting the rest of the project (or editor, for [EditorPlugin]s and [EditorScript]s).
+ [b]Note:[/b] [method delay_msec] is a [i]blocking[/i] way to delay code execution. To delay code execution in a non-blocking way, see [method SceneTree.create_timer]. Awaiting with [method SceneTree.create_timer] will delay the execution of code placed below the [code]await[/code] without affecting the rest of the project (or editor, for [EditorPlugin]s and [EditorScript]s).
[b]Note:[/b] When [method delay_msec] is called on the main thread, it will freeze the project and will prevent it from redrawing and registering input until the delay has passed. When using [method delay_msec] as part of an [EditorPlugin] or [EditorScript], it will freeze the editor but won't freeze the project if it is currently running (since the project is an independent child process).
</description>
</method>
@@ -66,7 +66,7 @@
<argument index="0" name="usec" type="int" />
<description>
Delays execution of the current thread by [code]usec[/code] microseconds. [code]usec[/code] must be greater than or equal to [code]0[/code]. Otherwise, [method delay_usec] will do nothing and will print an error message.
- [b]Note:[/b] [method delay_usec] is a [i]blocking[/i] way to delay code execution. To delay code execution in a non-blocking way, see [method SceneTree.create_timer]. Yielding with [method SceneTree.create_timer] will delay the execution of code placed below the [code]yield[/code] without affecting the rest of the project (or editor, for [EditorPlugin]s and [EditorScript]s).
+ [b]Note:[/b] [method delay_usec] is a [i]blocking[/i] way to delay code execution. To delay code execution in a non-blocking way, see [method SceneTree.create_timer]. Awaiting with [method SceneTree.create_timer] will delay the execution of code placed below the [code]await[/code] without affecting the rest of the project (or editor, for [EditorPlugin]s and [EditorScript]s).
[b]Note:[/b] When [method delay_usec] is called on the main thread, it will freeze the project and will prevent it from redrawing and registering input until the delay has passed. When using [method delay_usec] as part of an [EditorPlugin] or [EditorScript], it will freeze the editor but won't freeze the project if it is currently running (since the project is an independent child process).
</description>
</method>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index 5c64dc4162..a3616af999 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -209,6 +209,9 @@
<theme_item name="font_disabled_color" data_type="color" type="Color" default="Color(0.9, 0.9, 0.9, 0.2)">
Text [Color] used when the [OptionButton] is disabled.
</theme_item>
+ <theme_item name="font_focus_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
+ Text [Color] used when the [OptionButton] is focused. Only replaces the normal text color of the button. Disabled, hovered, and pressed states take precedence over this color.
+ </theme_item>
<theme_item name="font_hover_color" data_type="color" type="Color" default="Color(0.94, 0.94, 0.94, 1)">
Text [Color] used when the [OptionButton] is being hovered.
</theme_item>
diff --git a/doc/classes/PointLight2D.xml b/doc/classes/PointLight2D.xml
index 9c13179056..ec809ed0a8 100644
--- a/doc/classes/PointLight2D.xml
+++ b/doc/classes/PointLight2D.xml
@@ -8,7 +8,7 @@
</tutorials>
<members>
<member name="height" type="float" setter="set_height" getter="get_height" default="0.0">
- The height of the light. Used with 2D normal mapping.
+ The height of the light. Used with 2D normal mapping. The units are in pixels, e.g. if the height is 100, then it will illuminate an object 100 pixels away at a 45° angle to the plane.
</member>
<member name="offset" type="Vector2" setter="set_texture_offset" getter="get_texture_offset" default="Vector2(0, 0)">
The offset of the light's [member texture].
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index eec06d6f82..c646380bee 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -221,11 +221,6 @@
The project's name. It is used both by the Project Manager and by exporters. The project name can be translated by translating its value in localization files. The window title will be set to match the project name automatically on startup.
[b]Note:[/b] Changing this value will also change the user data folder's path if [member application/config/use_custom_user_dir] is [code]false[/code]. After renaming the project, you will no longer be able to access existing data in [code]user://[/code] unless you rename the old folder to match the new project name. See [url=https://docs.godotengine.org/en/latest/tutorials/io/data_paths.html]Data paths[/url] in the documentation for more information.
</member>
- <member name="application/config/project_data_dir_name" type="String" setter="" getter="" default="&quot;.godot&quot;">
- The project data directory is used for storing project-specific data (metadata, shader cache, etc.).
- [b]Note:[/b] Restart the application after changing this setting.
- [b]Note:[/b] Changing this value can help on platforms or with third-party tools where specific directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder.
- </member>
<member name="application/config/project_settings_override" type="String" setter="" getter="" default="&quot;&quot;">
Specifies a file to override project settings. For example: [code]user://custom_settings.cfg[/code]. See "Overriding" in the [ProjectSettings] class description at the top for more information.
[b]Note:[/b] Regardless of this setting's value, [code]res://override.cfg[/code] will still be read to override the project settings.
@@ -233,6 +228,12 @@
<member name="application/config/use_custom_user_dir" type="bool" setter="" getter="" default="false">
If [code]true[/code], the project will save user data to its own user directory (see [member application/config/custom_user_dir_name]). This setting is only effective on desktop platforms. A name must be set in the [member application/config/custom_user_dir_name] setting for this to take effect. If [code]false[/code], the project will save user data to [code](OS user data directory)/Godot/app_userdata/(project name)[/code].
</member>
+ <member name="application/config/use_hidden_project_data_directory" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], the project will use a hidden directory ([code].godot[/code]) for storing project-specific data (metadata, shader cache, etc.).
+ If [code]false[/code], a non-hidden directory ([code]godot[/code]) will be used instead.
+ [b]Note:[/b] Restart the application after changing this setting.
+ [b]Note:[/b] Changing this value can help on platforms or with third-party tools where hidden directory patterns are disallowed. Only modify this setting if you know that your environment requires it, as changing the default can impact compatibility with some external tools or plugins which expect the default [code].godot[/code] folder.
+ </member>
<member name="application/config/windows_native_icon" type="String" setter="" getter="" default="&quot;&quot;">
Icon set in [code].ico[/code] format used on Windows to set the game's icon. This is done automatically on start by calling [method DisplayServer.set_native_icon].
</member>
@@ -1698,8 +1699,10 @@
<member name="rendering/shadows/directional_shadow/size.mobile" type="int" setter="" getter="" default="2048">
Lower-end override for [member rendering/shadows/directional_shadow/size] on mobile devices, due to performance concerns or driver support.
</member>
- <member name="rendering/shadows/directional_shadow/soft_shadow_quality" type="int" setter="" getter="" default="2">
+ <member name="rendering/shadows/directional_shadow/soft_shadow_quality" type="int" setter="" getter="" default="3">
Quality setting for shadows cast by [DirectionalLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy.
+ [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance].
+ [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply [i]constant[/i] shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows.
</member>
<member name="rendering/shadows/directional_shadow/soft_shadow_quality.mobile" type="int" setter="" getter="" default="0">
Lower-end override for [member rendering/shadows/directional_shadow/soft_shadow_quality] on mobile devices, due to performance concerns or driver support.
@@ -1724,8 +1727,10 @@
<member name="rendering/shadows/shadow_atlas/size.mobile" type="int" setter="" getter="" default="2048">
Lower-end override for [member rendering/shadows/shadow_atlas/size] on mobile devices, due to performance concerns or driver support.
</member>
- <member name="rendering/shadows/shadows/soft_shadow_quality" type="int" setter="" getter="" default="2">
+ <member name="rendering/shadows/shadows/soft_shadow_quality" type="int" setter="" getter="" default="3">
Quality setting for shadows cast by [OmniLight3D]s and [SpotLight3D]s. Higher quality settings use more samples when reading from shadow maps and are thus slower. Low quality settings may result in shadows looking grainy.
+ [b]Note:[/b] The Soft Very Low setting will automatically multiply [i]constant[/i] shadow blur by 0.75x to reduce the amount of noise visible. This automatic blur change only affects the constant blur factor defined in [member Light3D.shadow_blur], not the variable blur performed by [DirectionalLight3D]s' [member Light3D.light_angular_distance].
+ [b]Note:[/b] The Soft High and Soft Ultra settings will automatically multiply shadow blur by 1.5× and 2× respectively to make better use of the increased sample count. This increased blur also improves stability of dynamic object shadows.
</member>
<member name="rendering/shadows/shadows/soft_shadow_quality.mobile" type="int" setter="" getter="" default="0">
Lower-end override for [member rendering/shadows/shadows/soft_shadow_quality] on mobile devices, due to performance concerns or driver support.
@@ -1750,23 +1755,23 @@
</member>
<member name="rendering/textures/vram_compression/import_bptc" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the BPTC algorithm. This texture compression algorithm is only supported on desktop platforms, and only when using the Vulkan renderer.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/project_data_dir_name]).
+ [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_etc" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression algorithm. This algorithm doesn't support alpha channels in textures.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/project_data_dir_name]).
+ [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_etc2" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the Ericsson Texture Compression 2 algorithm. This texture compression algorithm is only supported when using the Vulkan renderer.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/project_data_dir_name]).
+ [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_pvrtc" type="bool" setter="" getter="" default="false">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the PowerVR Texture Compression algorithm. This texture compression algorithm is only supported on iOS.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/project_data_dir_name]).
+ [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/textures/vram_compression/import_s3tc" type="bool" setter="" getter="" default="true">
If [code]true[/code], the texture importer will import VRAM-compressed textures using the S3 Texture Compression algorithm. This algorithm is only supported on desktop platforms and consoles.
- [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/project_data_dir_name]).
+ [b]Note:[/b] Changing this setting does [i]not[/i] impact textures that were already imported before. To make this setting apply to textures that were already imported, exit the editor, remove the [code].godot/imported/[/code] folder located inside the project folder then restart the editor (see [member application/config/use_hidden_project_data_directory]).
</member>
<member name="rendering/vulkan/descriptor_pools/max_descriptors_per_pool" type="int" setter="" getter="" default="64">
</member>
diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml
index 7858ac732b..9c948ca21a 100644
--- a/doc/classes/Quaternion.xml
+++ b/doc/classes/Quaternion.xml
@@ -43,9 +43,8 @@
</method>
<method name="Quaternion" qualifiers="constructor">
<return type="Quaternion" />
- <argument index="0" name="euler" type="Vector3" />
+ <argument index="0" name="euler_yxz" type="Vector3" />
<description>
- Constructs a quaternion that will perform a rotation specified by Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last), given in the vector format as (X angle, Y angle, Z angle).
</description>
</method>
<method name="Quaternion" qualifiers="constructor">
@@ -90,6 +89,16 @@
Returns the dot product of two quaternions.
</description>
</method>
+ <method name="get_angle" qualifiers="const">
+ <return type="float" />
+ <description>
+ </description>
+ </method>
+ <method name="get_axis" qualifiers="const">
+ <return type="Vector3" />
+ <description>
+ </description>
+ </method>
<method name="get_euler" qualifiers="const">
<return type="Vector3" />
<description>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 6f19114c16..973f8cdb1e 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3663,16 +3663,25 @@
Use 4 splits for shadow projection when using directional light.
</constant>
<constant name="SHADOW_QUALITY_HARD" value="0" enum="ShadowQuality">
+ Lowest shadow filtering quality (fastest). Soft shadows are not available with this quality setting, which means the [member Light3D.shadow_blur] property is ignored if [member Light3D.light_size] and [member Light3D.light_angular_distance] is [code]0.0[/code].
+ [b]Note:[/b] The variable shadow blur performed by [member Light3D.light_size] and [member Light3D.light_angular_distance] is still effective when using hard shadow filtering. In this case, [member Light3D.shadow_blur] [i]is[/i] taken into account. However, the results will not be blurred, instead the blur amount is treated as a maximum radius for the penumbra.
</constant>
- <constant name="SHADOW_QUALITY_SOFT_LOW" value="1" enum="ShadowQuality">
+ <constant name="SHADOW_QUALITY_SOFT_VERY_LOW" value="1" enum="ShadowQuality">
+ Very low shadow filtering quality (faster). When using this quality setting, [member Light3D.shadow_blur] is automatically multiplied by 0.75× to avoid introducing too much noise. This division only applies to lights whose [member Light3D.light_size] or [member Light3D.light_angular_distance] is [code]0.0[/code]).
</constant>
- <constant name="SHADOW_QUALITY_SOFT_MEDIUM" value="2" enum="ShadowQuality">
+ <constant name="SHADOW_QUALITY_SOFT_LOW" value="2" enum="ShadowQuality">
+ Low shadow filtering quality (fast).
</constant>
- <constant name="SHADOW_QUALITY_SOFT_HIGH" value="3" enum="ShadowQuality">
+ <constant name="SHADOW_QUALITY_SOFT_MEDIUM" value="3" enum="ShadowQuality">
+ Medium low shadow filtering quality (average).
</constant>
- <constant name="SHADOW_QUALITY_SOFT_ULTRA" value="4" enum="ShadowQuality">
+ <constant name="SHADOW_QUALITY_SOFT_HIGH" value="4" enum="ShadowQuality">
+ High low shadow filtering quality (slow). When using this quality setting, [member Light3D.shadow_blur] is automatically multiplied by 1.5× to better make use of the high sample count. This increased blur also improves the stability of dynamic object shadows. This multiplier only applies to lights whose [member Light3D.light_size] or [member Light3D.light_angular_distance] is [code]0.0[/code]).
</constant>
- <constant name="SHADOW_QUALITY_MAX" value="5" enum="ShadowQuality">
+ <constant name="SHADOW_QUALITY_SOFT_ULTRA" value="5" enum="ShadowQuality">
+ Highest low shadow filtering quality (slowest). When using this quality setting, [member Light3D.shadow_blur] is automatically multiplied by 2× to better make use of the high sample count. This increased blur also improves the stability of dynamic object shadows. This multiplier only applies to lights whose [member Light3D.light_size] or [member Light3D.light_angular_distance] is [code]0.0[/code]).
+ </constant>
+ <constant name="SHADOW_QUALITY_MAX" value="6" enum="ShadowQuality">
</constant>
<constant name="REFLECTION_PROBE_UPDATE_ONCE" value="0" enum="ReflectionProbeUpdateMode">
Reflection probe will update reflections once and then stop.
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 4ad9573196..0fd25615ba 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -36,10 +36,10 @@
</description>
</method>
<method name="append_text">
- <return type="int" enum="Error" />
+ <return type="void" />
<argument index="0" name="bbcode" type="String" />
<description>
- Parses [code]bbcode[/code] and adds tags to the tag stack as needed. Returns the result of the parsing, [constant OK] if successful.
+ Parses [code]bbcode[/code] and adds tags to the tag stack as needed.
[b]Note:[/b] Using this method, you can't close a tag that was opened in a previous [method append_text] call. This is done to improve performance, especially when updating large RichTextLabels since rebuilding the whole BBCode every time would be slower. If you absolutely need to close a tag in a future method call, append the [member text] instead of using [method append_text].
</description>
</method>
@@ -130,10 +130,10 @@
</description>
</method>
<method name="parse_bbcode">
- <return type="int" enum="Error" />
+ <return type="void" />
<argument index="0" name="bbcode" type="String" />
<description>
- The assignment version of [method append_text]. Clears the tag stack and inserts the new content. Returns [constant OK] if parses [code]bbcode[/code] successfully.
+ The assignment version of [method append_text]. Clears the tag stack and inserts the new content.
</description>
</method>
<method name="parse_expressions_for_values">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index ce7fd293d5..6d0ec44b69 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -66,7 +66,7 @@
[gdscript]
func some_function():
print("start")
- yield(get_tree().create_timer(1.0), "timeout")
+ await get_tree().create_timer(1.0).timeout
print("end")
[/gdscript]
[csharp]
diff --git a/doc/classes/SceneTreeTimer.xml b/doc/classes/SceneTreeTimer.xml
index f97c5e42b5..b0509a322e 100644
--- a/doc/classes/SceneTreeTimer.xml
+++ b/doc/classes/SceneTreeTimer.xml
@@ -10,7 +10,7 @@
[gdscript]
func some_function():
print("Timer started.")
- yield(get_tree().create_timer(1.0), "timeout")
+ await get_tree().create_timer(1.0).timeout
print("Timer ended.")
[/gdscript]
[csharp]
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 10ce03c4b2..abb68e0d05 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -56,11 +56,11 @@
Converts a string containing a binary number into an integer. Binary strings can either be prefixed with [code]0b[/code] or not, and they can also start with a [code]-[/code] before the optional prefix.
[codeblocks]
[gdscript]
- print("0x101".bin_to_int()) # Prints "5".
+ print("0b101".bin_to_int()) # Prints "5".
print("101".bin_to_int()) # Prints "5".
[/gdscript]
[csharp]
- GD.Print("0x101".BinToInt()); // Prints "5".
+ GD.Print("0b101".BinToInt()); // Prints "5".
GD.Print("101".BinToInt()); // Prints "5".
[/csharp]
[/codeblocks]
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index e65d5b4533..22d61c7285 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -16,6 +16,27 @@
<link title="2D Kinematic Character Demo">https://godotengine.org/asset-library/asset/113</link>
</tutorials>
<methods>
+ <method name="_tile_data_runtime_update" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <argument index="2" name="tile_data" type="TileData" />
+ <description>
+ Called with a TileData object about to be used internally by the TileMap, allowing its modification at runtime.
+ This method is only called if [method _use_tile_data_runtime_update] is implemented and returns [code]true[/code] for the given tile [code]coords[/coords] and [code]layer[/code].
+ [b]Warning:[/b] The [code]tile_data[/code] object's sub-resources are the same as the one in the TileSet. Modifying them might impact the whole TileSet. Instead, make sure to duplicate those resources.
+ [b]Note:[/b] If the properties of [code]tile_data[/code] object should change over time, use [method force_update] to trigger a TileMap update.
+ </description>
+ </method>
+ <method name="_use_tile_data_runtime_update" qualifiers="virtual">
+ <return type="bool" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="coords" type="Vector2i" />
+ <description>
+ Should return [code]true[/code] if the tile at coordinates [code]coords[/coords] on layer [code]layer[/code] requires a runtime update.
+ [b]Warning:[/b] Make sure this function only return [code]true[/code] when needed. Any tile processed at runtime without a need for it will imply a significant performance penalty.
+ </description>
+ </method>
<method name="add_layer">
<return type="void" />
<argument index="0" name="to_position" type="int" />
@@ -42,6 +63,15 @@
Clears cells that do not exist in the tileset.
</description>
</method>
+ <method name="force_update">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" default="-1" />
+ <description>
+ Triggers an update of the TileMap. If [code]layer[/code] is provided, only updates the given layer.
+ [b]Note:[/b] The TileMap node updates automatically when one of its properties is modified. A manual update is only needed if runtime modifications (implemented in [method _tile_data_runtime_update]) need to be applied.
+ [b]Warning:[/b] Updating the TileMap is a performance demanding task. Limit occurences of those updates to the minimum and limit the amount tiles they impact (by segregating tiles updated often to a dedicated layer for example).
+ </description>
+ </method>
<method name="get_cell_alternative_tile" qualifiers="const">
<return type="int" />
<argument index="0" name="layer" type="int" />
@@ -204,6 +234,17 @@
- The alternative tile identifier [code]alternative_tile[/code] identifies a tile alternative the source is a [TileSetAtlasSource], and the scene for a [TileSetScenesCollectionSource].
</description>
</method>
+ <method name="set_cells_from_surrounding_terrains">
+ <return type="void" />
+ <argument index="0" name="layer" type="int" />
+ <argument index="1" name="cells" type="Vector2i[]" />
+ <argument index="2" name="terrain_set" type="int" />
+ <argument index="3" name="ignore_empty_terrains" type="bool" default="true" />
+ <description>
+ Updates all the cells in the [code]cells[/code] coordinates array and replace them by tiles that matches the surrounding cells terrains. Only cells form the given [code]terrain_set[/code] are considered.
+ If [code]ignore_empty_terrains[/code] is true, zones with no terrain defined are ignored to select the tiles.
+ </description>
+ </method>
<method name="set_layer_enabled">
<return type="void" />
<argument index="0" name="layer" type="int" />
diff --git a/doc/classes/Time.xml b/doc/classes/Time.xml
index 24ce30154d..d8e1d874f7 100644
--- a/doc/classes/Time.xml
+++ b/doc/classes/Time.xml
@@ -6,7 +6,7 @@
<description>
The Time singleton allows converting time between various formats and also getting time information from the system.
This class conforms with as many of the ISO 8601 standards as possible. All dates follow the Proleptic Gregorian calendar. As such, the day before [code]1582-10-15[/code] is [code]1582-10-14[/code], not [code]1582-10-04[/code]. The year before 1 AD (aka 1 BC) is number [code]0[/code], with the year before that (2 BC) being [code]-1[/code], etc.
- Conversion methods assume "the same timezone", and do not handle timezone conversions or DST automatically. Leap seconds are also not handled, they must be done manually if desired. Suffixes such as "Z" are not handled, you need to strip them away manually.
+ Conversion methods assume "the same timezone", and do not handle timezone conversions or DST automatically. Unix epoch assumes UTC. Leap seconds are also not handled, they must be done manually if desired. Suffixes such as "Z" are not handled, you need to strip them away manually.
[b]Important:[/b] The [code]_from_system[/code] methods use the system clock that the user can manually set. [b]Never use[/b] this method for precise time calculation since its results are subject to automatic adjustments by the user or the operating system. [b]Always use[/b] [method get_ticks_usec] or [method get_ticks_msec] for precise time calculation instead, since they are guaranteed to be monotonic (i.e. never decrease).
</description>
<tutorials>
@@ -100,12 +100,14 @@
<return type="int" />
<description>
Returns the amount of time passed in milliseconds since the engine started.
+ Will always be positive or 0 and uses a 64-bit value (it will wrap after roughly 500 million years).
</description>
</method>
<method name="get_ticks_usec" qualifiers="const">
<return type="int" />
<description>
Returns the amount of time passed in microseconds since the engine started.
+ Will always be positive or 0 and uses a 64-bit value (it will wrap after roughly half a million years).
</description>
</method>
<method name="get_time_dict_from_system" qualifiers="const">
@@ -150,8 +152,9 @@
<description>
Converts a dictionary of time values to a Unix timestamp.
The given dictionary can be populated with the following keys: [code]year[/code], [code]month[/code], [code]day[/code], [code]hour[/code], [code]minute[/code], and [code]second[/code]. Any other entries (including [code]dst[/code]) are ignored.
- If the dictionary is empty, [code]0[/code] is returned. If some keys are omitted, they default to the equivalent values for the Unix epoch timestamp 0 (1970-01-01 at 00:00:00).
+ If the dictionary is empty, [code]0[/code] is returned. If some keys are omitted, they default to the equivalent values for the Unix epoch timestamp 0 (1970-01-01 at 00:00:00 UTC).
You can pass the output from [method get_datetime_dict_from_unix_time] directly into this function and get the same as what was put in.
+ [b]Note:[/b] Unix timestamps are usually in UTC, the given datetime dict may not be.
</description>
</method>
<method name="get_unix_time_from_datetime_string" qualifiers="const">
@@ -159,12 +162,13 @@
<argument index="0" name="datetime" type="String" />
<description>
Converts the given ISO 8601 date and/or time string to a Unix timestamp. The string can contain a date only, a time only, or both.
+ [b]Note:[/b] Unix timestamps are usually in UTC, the given datetime string may not be.
</description>
</method>
<method name="get_unix_time_from_system" qualifiers="const">
<return type="float" />
<description>
- Returns the current Unix timestamp in seconds based on the system time.
+ Returns the current Unix timestamp in seconds based on the system time in UTC.
</description>
</method>
</methods>
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 267a0d2e9e..078ba1e1a4 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -208,6 +208,17 @@
Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
+ <method name="octahedron_decode" qualifiers="static">
+ <return type="Vector3" />
+ <argument index="0" name="uv" type="Vector2" />
+ <description>
+ </description>
+ </method>
+ <method name="octahedron_encode" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ </description>
+ </method>
<method name="operator !=" qualifiers="operator">
<return type="bool" />
<description>
diff --git a/doc/tools/makerst.py b/doc/tools/make_rst.py
index 1ec9627c2c..ad9e5f4897 100755
--- a/doc/tools/makerst.py
+++ b/doc/tools/make_rst.py
@@ -1,5 +1,7 @@
#!/usr/bin/env python3
+# This script makes RST files from the XML class reference for use with the online docs.
+
import argparse
import os
import re
@@ -399,7 +401,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
# Warn contributors not to edit this file directly
f.write(":github_url: hide\n\n")
- f.write(".. Generated automatically by doc/tools/makerst.py in Godot's source tree.\n")
+ f.write(".. Generated automatically by doc/tools/make_rst.py in Godot's source tree.\n")
f.write(".. DO NOT EDIT THIS FILE, but the " + class_name + ".xml source instead.\n")
f.write(".. The source is found in doc/classes or modules/<name>/doc_classes.\n\n")
diff --git a/doc/translations/extract.py b/doc/translations/extract.py
index a13680a613..68ac5d59f5 100644
--- a/doc/translations/extract.py
+++ b/doc/translations/extract.py
@@ -24,7 +24,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\\n"
"""
-# Some strings used by makerst.py are normally part of the editor translations,
+# Some strings used by make_rst.py are normally part of the editor translations,
# so we need to include them manually here for the online docs.
BASE_STRINGS = [
"Description",
@@ -225,7 +225,7 @@ def _generate_translation_catalog_file(unique_msgs, output):
with open(output, "w", encoding="utf8") as f:
f.write(HEADER)
for msg in BASE_STRINGS:
- f.write("#: doc/tools/makerst.py\n")
+ f.write("#: doc/tools/make_rst.py\n")
f.write('msgid "{}"\n'.format(msg))
f.write('msgstr ""\n\n')
for msg in unique_msgs:
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 5e2431d44e..2488439738 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -371,7 +371,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
unsigned int write_ofs = 0;
size_t avail_bytes = 0;
- uint32_t default_device_msec = OS::get_singleton()->get_ticks_msec();
+ uint64_t default_device_msec = OS::get_singleton()->get_ticks_msec();
while (!ad->exit_thread) {
size_t read_bytes = 0;
@@ -463,7 +463,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
// If we're using the default device check that the current device is still the default
if (ad->device_name == "Default") {
- uint32_t msec = OS::get_singleton()->get_ticks_msec();
+ uint64_t msec = OS::get_singleton()->get_ticks_msec();
if (msec > (default_device_msec + 1000)) {
String old_default_device = ad->default_device;
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp
index 363d542fd5..fc7e7389d5 100644
--- a/editor/action_map_editor.cpp
+++ b/editor/action_map_editor.cpp
@@ -571,7 +571,13 @@ void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p
for (int i = 0; i < MOD_MAX; i++) {
mod_checkboxes[i]->set_pressed(false);
}
- physical_key_checkbox->set_pressed(false);
+
+ // Enable the Physical Key checkbox by default to encourage its use.
+ // Physical Key should be used for most game inputs as it allows keys to work
+ // on non-QWERTY layouts out of the box.
+ // This is especially important for WASD movement layouts.
+ physical_key_checkbox->set_pressed(true);
+
store_command_checkbox->set_pressed(true);
_set_current_device(0);
@@ -702,7 +708,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() {
physical_key_checkbox = memnew(CheckBox);
physical_key_checkbox->set_text(TTR("Use Physical Keycode"));
- physical_key_checkbox->set_tooltip(TTR("Stores the physical position of the key on the keyboard rather than the keys value. Used for compatibility with non-latin layouts."));
+ physical_key_checkbox->set_tooltip(TTR("Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts.\nThis should generally be enabled for most game shortcuts, but not in non-game applications."));
physical_key_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_physical_keycode_toggled));
physical_key_checkbox->hide();
additional_options_container->add_child(physical_key_checkbox);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 7f118532c9..a85a4450a6 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -584,7 +584,8 @@ public:
} break;
case Animation::TYPE_METHOD: {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name"));
- p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
+ static_assert(VARIANT_ARG_MAX == 8, "PROPERTY_HINT_RANGE needs to be updated if VARIANT_ARG_MAX != 8");
+ p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,8,1"));
Dictionary d = animation->track_get_key_value(track, key);
ERR_FAIL_COND(!d.has("args"));
@@ -1247,7 +1248,8 @@ public:
} break;
case Animation::TYPE_METHOD: {
p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name"));
- p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,5,1"));
+ static_assert(VARIANT_ARG_MAX == 8, "PROPERTY_HINT_RANGE needs to be updated if VARIANT_ARG_MAX != 8");
+ p_list->push_back(PropertyInfo(Variant::INT, "arg_count", PROPERTY_HINT_RANGE, "0,8,1"));
Dictionary d = animation->track_get_key_value(first_track, first_key);
ERR_FAIL_COND(!d.has("args"));
@@ -2037,7 +2039,7 @@ void AnimationTrackEdit::_notification(int p_what) {
update_mode_rect.position.y = int(get_size().height - update_icon->get_height()) / 2;
update_mode_rect.size = update_icon->get_size();
- if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ if (!animation->track_is_compressed(track) && animation->track_get_type(track) == Animation::TYPE_VALUE) {
draw_texture(update_icon, update_mode_rect.position);
}
// Make it easier to click.
@@ -2079,7 +2081,7 @@ void AnimationTrackEdit::_notification(int p_what) {
interp_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
interp_mode_rect.size = icon->get_size();
- if (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) {
+ 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, interp_mode_rect.position);
}
// Make it easier to click.
@@ -2089,7 +2091,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
interp_mode_rect.size.x += hsep;
- if (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) {
+ 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));
interp_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2112,7 +2114,7 @@ void AnimationTrackEdit::_notification(int p_what) {
loop_mode_rect.position.y = int(get_size().height - icon->get_height()) / 2;
loop_mode_rect.size = icon->get_size();
- if (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) {
+ 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);
}
@@ -2122,7 +2124,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep;
loop_mode_rect.size.x += hsep;
- if (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) {
+ 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();
} else {
@@ -2137,7 +2139,7 @@ void AnimationTrackEdit::_notification(int p_what) {
{
// Erase.
- Ref<Texture2D> icon = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
+ Ref<Texture2D> icon = get_theme_icon(animation->track_is_compressed(track) ? SNAME("Lock") : SNAME("Remove"), SNAME("EditorIcons"));
remove_rect.position.x = ofs + ((get_size().width - ofs) - icon->get_width()) / 2;
remove_rect.position.y = int(get_size().height - icon->get_height()) / 2;
@@ -2709,60 +2711,63 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
// Check keyframes.
- float scale = timeline->get_zoom_scale();
- int limit = timeline->get_name_limit();
- int limit_end = get_size().width - timeline->get_buttons_width();
- // Left Border including space occupied by keyframes on t=0.
- int limit_start_hitbox = limit - type_icon->get_width();
-
- if (pos.x >= limit_start_hitbox && pos.x <= limit_end) {
- int key_idx = -1;
- float key_distance = 1e20;
+ if (!animation->track_is_compressed(track)) { // Selecting compressed keyframes for editing is not possible.
- // Select should happen in the opposite order of drawing for more accurate overlap select.
- for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) {
- Rect2 rect = get_key_rect(i, scale);
- float offset = animation->track_get_key_time(track, i) - timeline->get_value();
- offset = offset * scale + limit;
- rect.position.x += offset;
-
- if (rect.has_point(pos)) {
- if (is_key_selectable_by_distance()) {
- float distance = ABS(offset - pos.x);
- if (key_idx == -1 || distance < key_distance) {
+ float scale = timeline->get_zoom_scale();
+ int limit = timeline->get_name_limit();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+ // Left Border including space occupied by keyframes on t=0.
+ int limit_start_hitbox = limit - type_icon->get_width();
+
+ if (pos.x >= limit_start_hitbox && pos.x <= limit_end) {
+ int key_idx = -1;
+ float key_distance = 1e20;
+
+ // Select should happen in the opposite order of drawing for more accurate overlap select.
+ for (int i = animation->track_get_key_count(track) - 1; i >= 0; i--) {
+ Rect2 rect = get_key_rect(i, scale);
+ float offset = animation->track_get_key_time(track, i) - timeline->get_value();
+ offset = offset * scale + limit;
+ rect.position.x += offset;
+
+ if (rect.has_point(pos)) {
+ if (is_key_selectable_by_distance()) {
+ float distance = ABS(offset - pos.x);
+ if (key_idx == -1 || distance < key_distance) {
+ key_idx = i;
+ key_distance = distance;
+ }
+ } else {
+ // First one does it.
key_idx = i;
- key_distance = distance;
+ break;
}
- } else {
- // First one does it.
- key_idx = i;
- break;
}
}
- }
- if (key_idx != -1) {
- if (mb->is_command_pressed() || mb->is_shift_pressed()) {
- if (editor->is_key_selected(track, key_idx)) {
- emit_signal(SNAME("deselect_key"), key_idx);
+ if (key_idx != -1) {
+ if (mb->is_command_pressed() || mb->is_shift_pressed()) {
+ if (editor->is_key_selected(track, key_idx)) {
+ emit_signal(SNAME("deselect_key"), key_idx);
+ } else {
+ emit_signal(SNAME("select_key"), key_idx, false);
+ moving_selection_attempt = true;
+ select_single_attempt = -1;
+ moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale();
+ }
} else {
- emit_signal(SNAME("select_key"), key_idx, false);
+ if (!editor->is_key_selected(track, key_idx)) {
+ emit_signal(SNAME("select_key"), key_idx, true);
+ select_single_attempt = -1;
+ } else {
+ select_single_attempt = key_idx;
+ }
+
moving_selection_attempt = true;
- select_single_attempt = -1;
moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale();
}
- } else {
- if (!editor->is_key_selected(track, key_idx)) {
- emit_signal(SNAME("select_key"), key_idx, true);
- select_single_attempt = -1;
- } else {
- select_single_attempt = key_idx;
- }
-
- moving_selection_attempt = true;
- moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale();
+ accept_event();
}
- accept_event();
}
}
}
@@ -2994,6 +2999,9 @@ void AnimationTrackEdit::set_in_group(bool p_enable) {
}
void AnimationTrackEdit::append_to_selection(const Rect2 &p_box, bool p_deselection) {
+ if (animation->track_is_compressed(track)) {
+ return; // Compressed keyframes can't be edited
+ }
// Left Border including space occupied by keyframes on t=0.
int limit_start_hitbox = timeline->get_name_limit() - type_icon->get_width();
Rect2 select_rect(limit_start_hitbox, 0, get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(), get_size().height);
@@ -3337,6 +3345,10 @@ void AnimationTrackEditor::_timeline_changed(float p_new_pos, bool p_drag, bool
}
void AnimationTrackEditor::_track_remove_request(int p_track) {
+ if (animation->track_is_compressed(p_track)) {
+ EditorNode::get_singleton()->show_warning(TTR("Compressed tracks can't be edited or removed. Re-import the animation with compression disabled in order to edit."));
+ return;
+ }
int idx = p_track;
if (idx >= 0 && idx < animation->get_track_count()) {
undo_redo->create_action(TTR("Remove Anim Track"));
@@ -3487,7 +3499,7 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
for (const InsertData &E : insert_data) {
// Prevent insertion of multiple tracks.
- if (E.path == p_id.path) {
+ if (E.path == p_id.path && E.type == p_id.type) {
return; // Already inserted a track this frame.
}
}
@@ -3537,7 +3549,11 @@ void AnimationTrackEditor::_insert_track(bool p_create_reset, bool p_create_bezi
}
}
-void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform) {
+void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value) {
+ ERR_FAIL_COND(!root);
+ ERR_FAIL_COND_MSG(
+ (p_type != Animation::TYPE_POSITION_3D && p_type != Animation::TYPE_ROTATION_3D && p_type != Animation::TYPE_SCALE_3D),
+ "Track type must be Position/Rotation/Scale 3D.");
if (!keying) {
return;
}
@@ -3545,7 +3561,6 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
return;
}
- ERR_FAIL_COND(!root);
// Let's build a node path.
String path = root->get_path_to(p_node);
if (p_sub != "") {
@@ -3554,24 +3569,16 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
NodePath np = path;
- int position_idx = -1;
- int rotation_idx = -1;
- int scale_idx = -1;
+ int track_idx = -1;
for (int i = 0; i < animation->get_track_count(); i++) {
if (animation->track_get_path(i) != np) {
continue;
}
-
- if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
- position_idx = i;
- }
- if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
- rotation_idx = i;
- }
- if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
- scale_idx = i;
+ if (animation->track_get_type(i) != p_type) {
+ continue;
}
+ track_idx = i;
}
InsertData id;
@@ -3579,48 +3586,30 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
// TRANSLATORS: This describes the target of new animation track, will be inserted into another string.
id.query = vformat(TTR("node '%s'"), p_node->get_name());
id.advance = false;
-
- {
- id.track_idx = position_idx;
- id.value = p_xform.origin;
- id.type = Animation::TYPE_POSITION_3D;
- _query_insert(id);
- }
- {
- id.track_idx = rotation_idx;
- id.value = p_xform.basis.get_rotation_quaternion();
- id.type = Animation::TYPE_ROTATION_3D;
- _query_insert(id);
- }
- {
- id.track_idx = scale_idx;
- id.value = p_xform.basis.get_scale();
- id.type = Animation::TYPE_SCALE_3D;
- _query_insert(id);
- }
+ id.track_idx = track_idx;
+ id.value = p_value;
+ id.type = p_type;
+ _query_insert(id);
}
-bool AnimationTrackEditor::has_transform_track(Node3D *p_node, const String &p_sub) {
+bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type) {
+ ERR_FAIL_COND_V(!root, false);
if (!keying) {
return false;
}
if (!animation.is_valid()) {
return false;
}
- if (!root) {
- return false;
- }
- //let's build a node path
+ // Let's build a node path.
String path = root->get_path_to(p_node);
if (p_sub != "") {
path += ":" + p_sub;
}
- int track_id = animation->find_track(path);
+
+ int track_id = animation->find_track(path, p_type);
if (track_id >= 0) {
- if (animation->track_get_type(track_id) == Animation::TYPE_POSITION_3D || animation->track_get_type(track_id) == Animation::TYPE_ROTATION_3D || animation->track_get_type(track_id) == Animation::TYPE_SCALE_3D) {
- return true;
- }
+ return true;
}
return false;
}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 2555901557..05cf91de1d 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -527,8 +527,8 @@ public:
void set_anim_pos(float p_pos);
void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
- void insert_transform_key(Node3D *p_node, const String &p_sub, const Transform3D &p_xform);
- bool has_transform_track(Node3D *p_node, const String &p_sub);
+ void insert_transform_key(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value);
+ bool has_track(Node3D *p_node, const String &p_sub, const Animation::TrackType p_type);
void make_insert_queue();
void commit_insert_queue();
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 4ee8b991e4..70ba806c37 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -419,7 +419,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
// Go through other track to find if animation is set
String animation_path = get_animation()->track_get_path(get_track());
animation_path = animation_path.replace(":frame", ":animation");
- int animation_track = get_animation()->find_track(animation_path);
+ int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
@@ -511,7 +511,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
// Go through other track to find if animation is set
String animation_path = get_animation()->track_get_path(get_track());
animation_path = animation_path.replace(":frame", ":animation");
- int animation_track = get_animation()->find_track(animation_path);
+ int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
diff --git a/editor/code_editor.h b/editor/code_editor.h
index 3c52a0c6e8..6e3bd88112 100644
--- a/editor/code_editor.h
+++ b/editor/code_editor.h
@@ -53,7 +53,6 @@ public:
void popup_find_line(CodeEdit *p_edit);
int get_line() const;
- void set_text_editor(CodeEdit *p_text_editor);
GotoLineDialog();
};
@@ -101,7 +100,6 @@ class FindReplaceBar : public HBoxContainer {
void _search_text_changed(const String &p_text);
void _search_text_submitted(const String &p_text);
void _replace_text_submitted(const String &p_text);
- void _update_size();
protected:
void _notification(int p_what);
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 9184ddcf39..976d718b8e 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -135,8 +135,6 @@ private:
Vector<Callable> undo_redo_callbacks;
Map<StringName, Callable> move_element_functions;
- void _cleanup_history();
-
Vector<EditedScene> edited_scene;
int current_edited_scene;
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 767856f939..3d6b523733 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -2163,7 +2163,8 @@ Error EditorFileSystem::_resource_import(const String &p_path) {
}
bool EditorFileSystem::_should_skip_directory(const String &p_path) {
- if (p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) {
+ String project_data_path = ProjectSettings::get_singleton()->get_project_data_path();
+ if (p_path == project_data_path || p_path.begins_with(project_data_path + "/")) {
return true;
}
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index e5d6315ef7..a644e3e991 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -67,7 +67,7 @@
m_name->add_data(FontJapanese); \
m_name->add_data(FontFallback);
-#define MAKE_DEFAULT_FONT(m_name, m_variations, m_base_size) \
+#define MAKE_DEFAULT_FONT(m_name, m_variations) \
Ref<Font> m_name; \
m_name.instantiate(); \
if (CustomFont.is_valid()) { \
@@ -89,12 +89,11 @@
} \
m_name->set_variation_coordinates(variations); \
} \
- m_name->set_base_size(m_base_size); \
m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS(m_name);
-#define MAKE_BOLD_FONT(m_name, m_variations, m_base_size) \
+#define MAKE_BOLD_FONT(m_name, m_variations) \
Ref<Font> m_name; \
m_name.instantiate(); \
if (CustomFontBold.is_valid()) { \
@@ -116,12 +115,11 @@
} \
m_name->set_variation_coordinates(variations); \
} \
- m_name->set_base_size(m_base_size); \
m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS_BOLD(m_name);
-#define MAKE_SOURCE_FONT(m_name, m_variations, m_base_size) \
+#define MAKE_SOURCE_FONT(m_name, m_variations) \
Ref<Font> m_name; \
m_name.instantiate(); \
if (CustomFontSource.is_valid()) { \
@@ -143,7 +141,6 @@
} \
m_name->set_variation_coordinates(variations); \
} \
- m_name->set_base_size(m_base_size); \
m_name->set_spacing(TextServer::SPACING_TOP, -EDSCALE); \
m_name->set_spacing(TextServer::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS(m_name);
@@ -275,7 +272,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
Ref<FontData> dfmono = load_cached_internal_font(_font_Hack_Regular, _font_Hack_Regular_size, font_hinting, font_antialiased, true);
// Default font
- MAKE_DEFAULT_FONT(df, String(), default_font_size);
+ MAKE_DEFAULT_FONT(df, String());
p_theme->set_default_theme_font(df); // Default theme font
p_theme->set_default_theme_font_size(default_font_size);
@@ -283,7 +280,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
p_theme->set_font("main", "EditorFonts", df);
// Bold font
- MAKE_BOLD_FONT(df_bold, String(), default_font_size);
+ MAKE_BOLD_FONT(df_bold, String());
p_theme->set_font_size("bold_size", "EditorFonts", default_font_size);
p_theme->set_font("bold", "EditorFonts", df_bold);
@@ -310,7 +307,7 @@ void editor_register_fonts(Ref<Theme> p_theme) {
// Documentation fonts
String code_font_custom_variations = EditorSettings::get_singleton()->get("interface/editor/code_font_custom_variations");
- MAKE_SOURCE_FONT(df_code, code_font_custom_variations, default_font_size);
+ MAKE_SOURCE_FONT(df_code, code_font_custom_variations);
p_theme->set_font_size("doc_size", "EditorFonts", int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
p_theme->set_font("doc", "EditorFonts", df);
p_theme->set_font_size("doc_bold_size", "EditorFonts", int(EDITOR_GET("text_editor/help/help_font_size")) * EDSCALE);
diff --git a/editor/editor_help.h b/editor/editor_help.h
index 7a45b1abc1..46605b6763 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -57,7 +57,6 @@ class FindBar : public HBoxContainer {
int results_count;
- void _show_search();
void _hide_bar();
void _search_text_changed(const String &p_text);
@@ -66,8 +65,6 @@ class FindBar : public HBoxContainer {
void _update_results_count();
void _update_matches_label();
- void _update_size();
-
protected:
void _notification(int p_what);
virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 06af8566ff..124ea31302 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -96,8 +96,6 @@ private:
bool draw_top_bg;
void _ensure_popup();
- bool _is_property_different(const Variant &p_current, const Variant &p_orig);
- bool _get_instantiated_node_original_property(const StringName &p_prop, Variant &value);
void _focusable_focused(int p_index);
bool selectable;
@@ -281,8 +279,6 @@ public:
void unfold();
void fold();
- Object *get_edited_object();
-
EditorInspectorSection();
~EditorInspectorSection();
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 891705da98..b4980fe074 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -562,9 +562,9 @@ void EditorNode::_notification(int p_what) {
last_checked_version = editor_data.get_undo_redo().get_version();
}
- // update the animation frame of the update spinner
+ // Update the animation frame of the update spinner.
uint64_t frame = Engine::get_singleton()->get_frames_drawn();
- uint32_t tick = OS::get_singleton()->get_ticks_msec();
+ uint64_t tick = OS::get_singleton()->get_ticks_msec();
if (frame != update_spinner_step_frame && (tick - update_spinner_step_msec) > (1000 / 8)) {
update_spinner_step++;
@@ -575,7 +575,7 @@ void EditorNode::_notification(int p_what) {
update_spinner_step_msec = tick;
update_spinner_step_frame = frame + 1;
- // update the icon itself only when the spinner is visible
+ // Update the icon itself only when the spinner is visible.
if (EditorSettings::get_singleton()->get("interface/editor/show_update_spinner")) {
update_spinner->set_icon(gui_base->get_theme_icon("Progress" + itos(update_spinner_step + 1), SNAME("EditorIcons")));
}
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 348db1cb7d..03e5146d98 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -407,7 +407,7 @@ private:
bool waiting_for_sources_changed;
- uint32_t update_spinner_step_msec;
+ uint64_t update_spinner_step_msec;
uint64_t update_spinner_step_frame;
int update_spinner_step;
@@ -520,9 +520,6 @@ private:
void _run(bool p_current = false, const String &p_custom = "");
void _run_native(const Ref<EditorExportPreset> &p_preset);
- void _save_optimized();
- void _import_action(const String &p_action);
- void _import(const String &p_file);
void _add_to_recent_scenes(const String &p_scene);
void _update_recent_scenes();
void _open_recent_scene(int p_idx);
@@ -560,7 +557,6 @@ private:
static void _editor_file_dialog_register(EditorFileDialog *p_dialog);
static void _editor_file_dialog_unregister(EditorFileDialog *p_dialog);
- void _cleanup_scene();
void _remove_edited_scene(bool p_change_tab = true);
void _remove_scene(int index, bool p_change_tab = true);
bool _find_and_save_resource(RES p_res, Map<RES, bool> &processed, int32_t flags);
@@ -658,8 +654,6 @@ private:
static int build_callback_count;
static EditorBuildCallback build_callbacks[MAX_BUILD_CALLBACKS];
- void _license_tree_selected();
-
void _update_update_spinner();
Vector<Ref<EditorResourceConversionPlugin>> resource_conversion_plugins;
@@ -778,7 +772,6 @@ public:
SubViewport *get_scene_root() { return scene_root; } // root of the scene being edited
void fix_dependencies(const String &p_for_file);
- void clear_scene() { _cleanup_scene(); }
int new_scene();
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
@@ -851,8 +844,6 @@ public:
bool is_scene_in_use(const String &p_path);
- void scan_import_changes();
-
void save_layout();
void open_export_template_manager();
@@ -894,7 +885,6 @@ public:
EditorNode();
~EditorNode();
- void get_singleton(const char *arg1, bool arg2);
void add_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
void remove_resource_conversion_plugin(const Ref<EditorResourceConversionPlugin> &p_plugin);
diff --git a/editor/editor_path.h b/editor/editor_path.h
index cabfa931d6..07f8b7244e 100644
--- a/editor/editor_path.h
+++ b/editor/editor_path.h
@@ -47,7 +47,6 @@ class EditorPath : public Button {
PopupMenu *sub_objects_menu;
Vector<ObjectID> objects;
- EditorPath();
void _show_popup();
void _id_pressed(int p_idx);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 2d4a3ac788..6a36304ba4 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -884,10 +884,11 @@ public:
flag_rects.push_back(rect2);
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
+ int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Vector2 offset;
offset.y = rect2.size.y * 0.75;
- draw_string(font, rect2.position + offset, itos(layer_index + 1), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color);
+ draw_string(font, rect2.position + offset, itos(layer_index + 1), HALIGN_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);
ofs.x += bsize + 1;
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index fdcc0438ae..06c39a957c 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -384,6 +384,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color font_color = mono_color.lerp(base_color, 0.25);
const Color font_hover_color = mono_color.lerp(base_color, 0.125);
+ const Color font_focus_color = mono_color.lerp(base_color, 0.125);
const Color font_disabled_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.3);
const Color font_readonly_color = Color(mono_color.r, mono_color.g, mono_color.b, 0.65);
const Color selection_color = accent_color * Color(1, 1, 1, 0.4);
@@ -392,6 +393,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
Color icon_hover_color = Color(1, 1, 1) * (dark_theme ? 1.15 : 1.45);
icon_hover_color.a = 1.0;
+ Color icon_focus_color = icon_hover_color;
// Make the pressed icon color overbright because icons are not completely white on a dark theme.
// On a light theme, icons are dark, so we need to modulate them with an even brighter color.
Color icon_pressed_color = accent_color * (dark_theme ? 1.15 : 3.5);
@@ -625,6 +627,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "MenuButton", font_color);
theme->set_color("font_hover_color", "MenuButton", font_hover_color);
+ theme->set_color("font_focus_color", "MenuButton", font_focus_color);
theme->set_stylebox("MenuHover", "EditorStyles", style_widget_hover);
@@ -637,9 +640,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "Button", font_color);
theme->set_color("font_hover_color", "Button", font_hover_color);
+ theme->set_color("font_focus_color", "Button", font_focus_color);
theme->set_color("font_pressed_color", "Button", accent_color);
theme->set_color("font_disabled_color", "Button", font_disabled_color);
theme->set_color("icon_hover_color", "Button", icon_hover_color);
+ theme->set_color("icon_focus_color", "Button", icon_focus_color);
theme->set_color("icon_pressed_color", "Button", icon_pressed_color);
// OptionButton
@@ -657,9 +662,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "OptionButton", font_color);
theme->set_color("font_hover_color", "OptionButton", font_hover_color);
+ theme->set_color("font_focus_color", "OptionButton", font_focus_color);
theme->set_color("font_pressed_color", "OptionButton", accent_color);
theme->set_color("font_disabled_color", "OptionButton", font_disabled_color);
theme->set_color("icon_hover_color", "OptionButton", icon_hover_color);
+ theme->set_color("icon_focus_color", "OptionButton", icon_focus_color);
theme->set_icon("arrow", "OptionButton", theme->get_icon("GuiOptionArrow", "EditorIcons"));
theme->set_constant("arrow_margin", "OptionButton", widget_default_margin.x - 2 * EDSCALE);
theme->set_constant("modulate_arrow", "OptionButton", true);
@@ -683,9 +690,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "CheckButton", font_color);
theme->set_color("font_hover_color", "CheckButton", font_hover_color);
+ theme->set_color("font_focus_color", "CheckButton", font_focus_color);
theme->set_color("font_pressed_color", "CheckButton", accent_color);
theme->set_color("font_disabled_color", "CheckButton", font_disabled_color);
theme->set_color("icon_hover_color", "CheckButton", icon_hover_color);
+ theme->set_color("icon_focus_color", "CheckButton", icon_focus_color);
theme->set_constant("hseparation", "CheckButton", 8 * EDSCALE);
theme->set_constant("check_vadjust", "CheckButton", 0 * EDSCALE);
@@ -712,9 +721,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "CheckBox", font_color);
theme->set_color("font_hover_color", "CheckBox", font_hover_color);
+ theme->set_color("font_focus_color", "CheckBox", font_focus_color);
theme->set_color("font_pressed_color", "CheckBox", accent_color);
theme->set_color("font_disabled_color", "CheckBox", font_disabled_color);
theme->set_color("icon_hover_color", "CheckBox", icon_hover_color);
+ theme->set_color("icon_focus_color", "CheckBox", icon_focus_color);
theme->set_constant("hseparation", "CheckBox", 8 * EDSCALE);
theme->set_constant("check_vadjust", "CheckBox", 0 * EDSCALE);
@@ -1219,6 +1230,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("focus", "LinkButton", style_empty);
theme->set_color("font_color", "LinkButton", font_color);
theme->set_color("font_hover_color", "LinkButton", font_hover_color);
+ theme->set_color("font_focus_color", "LinkButton", font_focus_color);
theme->set_color("font_pressed_color", "LinkButton", accent_color);
theme->set_color("font_disabled_color", "LinkButton", font_disabled_color);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 1d1976d7e5..cdf0f6e391 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -688,9 +688,11 @@ void FileSystemDock::_sort_file_info_list(List<FileSystemDock::FileInfo> &r_file
r_file_list.reverse();
break;
case FILE_SORT_NAME_REVERSE:
+ r_file_list.sort();
r_file_list.reverse();
break;
default: // FILE_SORT_NAME
+ r_file_list.sort();
break;
}
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 73bdd685b7..7c3851b94f 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -205,8 +205,6 @@ private:
void _set_file_display(bool p_active);
void _fs_changed();
- void _tree_toggle_collapsed();
-
void _select_file(const String &p_path, bool p_select_in_favorites = false);
void _tree_activate_file();
void _file_list_activate_file(int p_idx);
@@ -228,8 +226,6 @@ private:
void _file_removed(String p_file);
void _folder_removed(String p_folder);
- void _files_moved(String p_old_file, String p_new_file);
- void _folder_moved(String p_old_folder, String p_new_folder);
void _resource_created();
void _make_dir_confirm();
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
index b61f6e12eb..56356ff25b 100644
--- a/editor/find_in_files.cpp
+++ b/editor/find_in_files.cpp
@@ -47,13 +47,13 @@
const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found";
const char *FindInFiles::SIGNAL_FINISHED = "finished";
-// TODO Would be nice in Vector and Vectors
+// TODO: Would be nice in Vector and Vectors.
template <typename T>
inline void pop_back(T &container) {
container.resize(container.size() - 1);
}
-// TODO Copied from TextEdit private, would be nice to extract it in a single place
+// TODO: Copied from TextEdit private, would be nice to extract it in a single place.
static bool is_text_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
@@ -125,7 +125,7 @@ void FindInFiles::start() {
return;
}
- // Init search
+ // Init search.
_current_dir = "";
PackedStringArray init_folder;
init_folder.push_back(_root_dir);
@@ -145,14 +145,14 @@ void FindInFiles::stop() {
}
void FindInFiles::_process() {
- // This part can be moved to a thread if needed
+ // This part can be moved to a thread if needed.
OS &os = *OS::get_singleton();
- float time_before = os.get_ticks_msec();
+ uint64_t time_before = os.get_ticks_msec();
while (is_processing()) {
_iterate();
- float elapsed = (os.get_ticks_msec() - time_before);
- if (elapsed > 1000.0 / 120.0) {
+ uint64_t elapsed = (os.get_ticks_msec() - time_before);
+ if (elapsed > 8) { // Process again after waiting 8 ticks.
break;
}
}
@@ -160,12 +160,12 @@ void FindInFiles::_process() {
void FindInFiles::_iterate() {
if (_folders_stack.size() != 0) {
- // Scan folders first so we can build a list of files and have progress info later
+ // Scan folders first so we can build a list of files and have progress info later.
PackedStringArray &folders_to_scan = _folders_stack.write[_folders_stack.size() - 1];
if (folders_to_scan.size() != 0) {
- // Scan one folder below
+ // Scan one folder below.
String folder_name = folders_to_scan[folders_to_scan.size() - 1];
pop_back(folders_to_scan);
@@ -178,19 +178,19 @@ void FindInFiles::_iterate() {
_folders_stack.push_back(sub_dirs);
} else {
- // Go back one level
+ // Go back one level.
pop_back(_folders_stack);
_current_dir = _current_dir.get_base_dir();
if (_folders_stack.size() == 0) {
- // All folders scanned
+ // All folders scanned.
_initial_files_count = _files_to_scan.size();
}
}
} else if (_files_to_scan.size() != 0) {
- // Then scan files
+ // Then scan files.
String fpath = _files_to_scan[_files_to_scan.size() - 1];
pop_back(_files_to_scan);
@@ -228,14 +228,14 @@ void FindInFiles::_scan_dir(String path, PackedStringArray &out_folders) {
break;
}
- // If there is a .gdignore file in the directory, don't bother searching it
+ // If there is a .gdignore file in the directory, skip searching the directory.
if (file == ".gdignore") {
break;
}
- // Ignore special dirs (such as .git and project data directory)
+ // Ignore special directories (such as those beginning with . and the project data directory).
String project_data_dir_name = ProjectSettings::get_singleton()->get_project_data_dir_name();
- if (file.begins_with(".") || file.begins_with(project_data_dir_name)) {
+ if (file.begins_with(".") || file == project_data_dir_name) {
continue;
}
if (dir->current_is_hidden()) {
@@ -264,7 +264,7 @@ void FindInFiles::_scan_file(String fpath) {
int line_number = 0;
while (!f->eof_reached()) {
- // line number starts at 1
+ // Line number starts at 1.
++line_number;
int begin = 0;
@@ -331,7 +331,7 @@ FindInFilesDialog::FindInFilesDialog() {
_replace_text_line_edit->hide();
gc->add_child(_replace_text_line_edit);
- gc->add_child(memnew(Control)); // Space to maintain the grid aligned.
+ gc->add_child(memnew(Control)); // Space to maintain the grid alignment.
{
HBoxContainer *hbc = memnew(HBoxContainer);
@@ -421,7 +421,7 @@ void FindInFilesDialog::set_find_in_files_mode(FindInFilesMode p_mode) {
_replace_text_line_edit->show();
}
- // After hiding some child controls, let's recalculate proper Dialog size
+ // Recalculate the dialog size after hiding child controls.
set_size(Size2(get_size().x, 0));
}
@@ -448,7 +448,7 @@ String FindInFilesDialog::get_folder() const {
}
Set<String> FindInFilesDialog::get_filter() const {
- // could check the _filters_preferences but it might not have been generated yet.
+ // Could check the _filters_preferences but it might not have been generated yet.
Set<String> filters;
for (int i = 0; i < _filters_container->get_child_count(); ++i) {
CheckBox *cb = (CheckBox *)_filters_container->get_child(i);
@@ -492,6 +492,7 @@ void FindInFilesDialog::custom_action(const String &p_action) {
CheckBox *cb = (CheckBox *)_filters_container->get_child(i);
_filters_preferences[cb->get_text()] = cb->is_pressed();
}
+
if (p_action == "find") {
emit_signal(SNAME(SIGNAL_FIND_REQUESTED));
hide();
@@ -510,7 +511,7 @@ void FindInFilesDialog::_on_search_text_modified(String text) {
}
void FindInFilesDialog::_on_search_text_submitted(String text) {
- // This allows to trigger a global search without leaving the keyboard
+ // This allows to trigger a global search without leaving the keyboard.
if (!_find_button->is_disabled()) {
if (_mode == SEARCH_MODE) {
custom_action("find");
@@ -525,7 +526,7 @@ void FindInFilesDialog::_on_search_text_submitted(String text) {
}
void FindInFilesDialog::_on_replace_text_submitted(String text) {
- // This allows to trigger a global search without leaving the keyboard
+ // This allows to trigger a global search without leaving the keyboard.
if (!_replace_button->is_disabled()) {
if (_mode == REPLACE_MODE) {
custom_action("replace");
@@ -641,13 +642,12 @@ void FindInFilesPanel::set_with_replace(bool with_replace) {
_replace_container->set_visible(with_replace);
if (with_replace) {
- // Results show checkboxes on their left so they can be opted out
+ // Results show checkboxes on their left so they can be opted out.
_results_display->set_columns(2);
_results_display->set_column_expand(0, false);
_results_display->set_column_custom_minimum_width(0, 48 * EDSCALE);
-
} else {
- // Results are single-cell items
+ // Results are single-cell items.
_results_display->set_column_expand(0, true);
_results_display->set_columns(1);
}
@@ -708,12 +708,12 @@ void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin
file_item->set_text(0, fpath);
file_item->set_metadata(0, fpath);
- // The width of this column is restrained to checkboxes, but that doesn't make sense for the parent items,
- // so we override their width so they can expand to full width
+ // The width of this column is restrained to checkboxes,
+ // but that doesn't make sense for the parent items,
+ // so we override their width so they can expand to full width.
file_item->set_expand_right(0, true);
_file_items[fpath] = file_item;
-
} else {
file_item = E->value();
}
@@ -725,7 +725,7 @@ void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin
// Do this first because it resets properties of the cell...
item->set_cell_mode(text_index, TreeItem::CELL_MODE_CUSTOM);
- // Trim result item line
+ // Trim result item line.
int old_text_size = text.size();
text = text.strip_edges(true, false);
int chars_removed = old_text_size - text.size();
@@ -780,9 +780,8 @@ void FindInFilesPanel::_on_item_edited() {
if (item->is_checked(0)) {
item->set_custom_color(1, _results_display->get_theme_color(SNAME("font_color")));
-
} else {
- // Grey out
+ // Grey out.
Color color = _results_display->get_theme_color(SNAME("font_color"));
color.a /= 2.0;
item->set_custom_color(1, color);
@@ -857,19 +856,19 @@ void FindInFilesPanel::_on_replace_all_clicked() {
}
if (locations.size() != 0) {
- // Results are sorted by file, so we can batch replaces
+ // Results are sorted by file, so we can batch replaces.
apply_replaces_in_file(fpath, locations, replace_text);
modified_files.push_back(fpath);
}
}
- // Hide replace bar so we can't trigger the action twice without doing a new search
+ // Hide replace bar so we can't trigger the action twice without doing a new search.
_replace_container->hide();
emit_signal(SNAME(SIGNAL_FILES_MODIFIED), modified_files);
}
-// Same as get_line, but preserves line ending characters
+// Same as get_line, but preserves line ending characters.
class ConservativeGetLine {
public:
String get_line(FileAccess *f) {
@@ -941,7 +940,7 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result>
}
line = line.left(repl_begin) + new_text + line.substr(repl_end);
- // keep an offset in case there are successive replaces in the same line
+ // Keep an offset in case there are successive replaces in the same line.
offset += new_text.length() - (repl_end - repl_begin);
}
@@ -951,7 +950,7 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result>
buffer += conservative.get_line(f);
}
- // Now the modified contents are in the buffer, rewrite the file with our changes
+ // Now the modified contents are in the buffer, rewrite the file with our changes.
Error err = f->reopen(fpath, FileAccess::WRITE);
ERR_FAIL_COND_MSG(err != OK, "Cannot create file in path '" + fpath + "'.");
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index 113306fc8a..f01c7f50f7 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -204,7 +204,8 @@ void GroupDialog::_add_group(String p_name) {
TreeItem *new_group = groups->create_item(groups_root);
new_group->set_text(0, name);
- new_group->add_button(0, groups->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0);
+ new_group->add_button(0, groups->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), DELETE_GROUP);
+ new_group->add_button(0, groups->get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), COPY_GROUP);
new_group->set_editable(0, true);
new_group->select(0);
groups->ensure_cursor_is_visible();
@@ -297,43 +298,50 @@ void GroupDialog::_load_groups(Node *p_current) {
}
}
-void GroupDialog::_delete_group_pressed(Object *p_item, int p_column, int p_id) {
+void GroupDialog::_modify_group_pressed(Object *p_item, int p_column, int p_id) {
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
if (!ti) {
return;
}
- String name = ti->get_text(0);
+ switch (p_id) {
+ case DELETE_GROUP: {
+ String name = ti->get_text(0);
- undo_redo->create_action(TTR("Delete Group"));
+ undo_redo->create_action(TTR("Delete Group"));
- List<Node *> nodes;
- scene_tree->get_nodes_in_group(name, &nodes);
- bool removed_all = true;
- for (Node *E : nodes) {
- if (_can_edit(E, name)) {
- undo_redo->add_do_method(E, "remove_from_group", name);
- undo_redo->add_undo_method(E, "add_to_group", name, true);
- } else {
- removed_all = false;
- }
- }
+ List<Node *> nodes;
+ scene_tree->get_nodes_in_group(name, &nodes);
+ bool removed_all = true;
+ for (Node *E : nodes) {
+ if (_can_edit(E, name)) {
+ undo_redo->add_do_method(E, "remove_from_group", name);
+ undo_redo->add_undo_method(E, "add_to_group", name, true);
+ } else {
+ removed_all = false;
+ }
+ }
- if (removed_all) {
- undo_redo->add_do_method(this, "_delete_group_item", name);
- undo_redo->add_undo_method(this, "_add_group", name);
- }
+ if (removed_all) {
+ undo_redo->add_do_method(this, "_delete_group_item", name);
+ undo_redo->add_undo_method(this, "_add_group", name);
+ }
- undo_redo->add_do_method(this, "_group_selected");
- undo_redo->add_undo_method(this, "_group_selected");
- undo_redo->add_do_method(this, "emit_signal", "group_edited");
- undo_redo->add_undo_method(this, "emit_signal", "group_edited");
+ undo_redo->add_do_method(this, "_group_selected");
+ undo_redo->add_undo_method(this, "_group_selected");
+ undo_redo->add_do_method(this, "emit_signal", "group_edited");
+ undo_redo->add_undo_method(this, "emit_signal", "group_edited");
- // To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ // To force redraw of scene tree.
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->commit_action();
+ undo_redo->commit_action();
+ } break;
+ case COPY_GROUP: {
+ DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column));
+ } break;
+ }
}
void GroupDialog::_delete_group_item(const String &p_name) {
@@ -437,7 +445,7 @@ GroupDialog::GroupDialog() {
groups->set_v_size_flags(Control::SIZE_EXPAND_FILL);
groups->add_theme_constant_override("draw_guides", 1);
groups->connect("item_selected", callable_mp(this, &GroupDialog::_group_selected));
- groups->connect("button_pressed", callable_mp(this, &GroupDialog::_delete_group_pressed));
+ groups->connect("button_pressed", callable_mp(this, &GroupDialog::_modify_group_pressed));
groups->connect("item_edited", callable_mp(this, &GroupDialog::_group_renamed));
HBoxContainer *chbc = memnew(HBoxContainer);
@@ -582,7 +590,7 @@ void GroupsEditor::_add_group(const String &p_group) {
group_name->clear();
}
-void GroupsEditor::_remove_group(Object *p_item, int p_column, int p_id) {
+void GroupsEditor::_modify_group(Object *p_item, int p_column, int p_id) {
if (!node) {
return;
}
@@ -591,21 +599,26 @@ void GroupsEditor::_remove_group(Object *p_item, int p_column, int p_id) {
if (!ti) {
return;
}
+ switch (p_id) {
+ case DELETE_GROUP: {
+ String name = ti->get_text(0);
+ undo_redo->create_action(TTR("Remove from Group"));
- String name = ti->get_text(0);
-
- undo_redo->create_action(TTR("Remove from Group"));
-
- undo_redo->add_do_method(node, "remove_from_group", name);
- undo_redo->add_undo_method(node, "add_to_group", name, true);
- undo_redo->add_do_method(this, "update_tree");
- undo_redo->add_undo_method(this, "update_tree");
+ undo_redo->add_do_method(node, "remove_from_group", name);
+ undo_redo->add_undo_method(node, "add_to_group", name, true);
+ undo_redo->add_do_method(this, "update_tree");
+ undo_redo->add_undo_method(this, "update_tree");
- // To force redraw of scene tree.
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ // To force redraw of scene tree.
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
- undo_redo->commit_action();
+ undo_redo->commit_action();
+ } break;
+ case COPY_GROUP: {
+ DisplayServer::get_singleton()->clipboard_set(ti->get_text(p_column));
+ } break;
+ }
}
struct _GroupInfoComparator {
@@ -653,7 +666,8 @@ void GroupsEditor::update_tree() {
TreeItem *item = tree->create_item(root);
item->set_text(0, gi.name);
if (can_be_deleted) {
- item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0);
+ item->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), DELETE_GROUP);
+ item->add_button(0, get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), COPY_GROUP);
} else {
item->set_selectable(0, false);
}
@@ -706,7 +720,7 @@ GroupsEditor::GroupsEditor() {
tree->set_hide_root(true);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vbc->add_child(tree);
- tree->connect("button_pressed", callable_mp(this, &GroupsEditor::_remove_group));
+ tree->connect("button_pressed", callable_mp(this, &GroupsEditor::_modify_group));
tree->add_theme_constant_override("draw_guides", 1);
add_theme_constant_override("separation", 3 * EDSCALE);
}
diff --git a/editor/groups_editor.h b/editor/groups_editor.h
index 69f746801f..f0a5b4a794 100644
--- a/editor/groups_editor.h
+++ b/editor/groups_editor.h
@@ -82,7 +82,7 @@ class GroupDialog : public AcceptDialog {
void _rename_group_item(const String &p_old_name, const String &p_new_name);
void _add_group(String p_name);
- void _delete_group_pressed(Object *p_item, int p_column, int p_id);
+ void _modify_group_pressed(Object *p_item, int p_column, int p_id);
void _delete_group_item(const String &p_name);
bool _can_edit(Node *p_node, String p_group);
@@ -95,6 +95,11 @@ protected:
static void _bind_methods();
public:
+ enum ModifyButton {
+ DELETE_GROUP,
+ COPY_GROUP,
+ };
+
void edit();
void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
@@ -116,8 +121,7 @@ class GroupsEditor : public VBoxContainer {
void update_tree();
void _add_group(const String &p_group = "");
- void _remove_group(Object *p_item, int p_column, int p_id);
- void _close();
+ void _modify_group(Object *p_item, int p_column, int p_id);
void _show_group_dialog();
@@ -125,6 +129,11 @@ protected:
static void _bind_methods();
public:
+ enum ModifyButton {
+ DELETE_GROUP,
+ COPY_GROUP,
+ };
+
void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; }
void set_current(Node *p_node);
diff --git a/editor/icons/NewKey.svg b/editor/icons/NewKey.svg
new file mode 100644
index 0000000000..fc8507e6c4
--- /dev/null
+++ b/editor/icons/NewKey.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="m13 9h-2v2h-2v2h2v2h2v-2h2v-2h-2z"/><path d="m10 9.723c-.596-.347-1-.985-1-1.723 0-1.104.896-2 2-2s2 .896 2 2h1v2h.445c.344-.591.555-1.268.555-2 0-2.209-1.791-4-4-4-1.822.002-3.414 1.235-3.869 3h-6.131v2h1v2h3v-2h2.133c.16.62.466 1.169.867 1.627v-.627h2z"/></g></svg>
diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h
index 86e9c5bde8..7d8fb3cac5 100644
--- a/editor/import/resource_importer_layered_texture.h
+++ b/editor/import/resource_importer_layered_texture.h
@@ -91,8 +91,6 @@ public:
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
- void update_imports();
-
virtual bool are_import_settings_valid(const String &p_path) const override;
virtual String get_import_settings_string() const override;
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 319e5ee25f..bebf05d481 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -950,6 +950,13 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, Map<Ref<
}
}
}
+
+ bool use_compression = node_settings["compression/enabled"];
+ int anim_compression_page_size = node_settings["compression/page_size"];
+
+ if (use_compression) {
+ _compress_animations(ap, anim_compression_page_size);
+ }
}
return p_node;
@@ -1149,6 +1156,15 @@ void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_
}
}
+void ResourceImporterScene::_compress_animations(AnimationPlayer *anim, int p_page_size_kb) {
+ List<StringName> anim_names;
+ anim->get_animation_list(&anim_names);
+ for (const StringName &E : anim_names) {
+ Ref<Animation> a = anim->get_animation(E);
+ a->compress(p_page_size_kb * 1024);
+ }
+}
+
void ResourceImporterScene::get_internal_import_options(InternalImportCategory p_category, List<ImportOption> *r_options) const {
switch (p_category) {
case INTERNAL_IMPORT_CATEGORY_NODE: {
@@ -1212,6 +1228,8 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compression/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compression/page_size", PROPERTY_HINT_RANGE, "4,512,1,suffix:kb"), 8));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/rotation", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/scale", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1));
@@ -1320,13 +1338,16 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor
}
} break;
case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {
- if (p_option.begins_with("animation/optimizer/") && p_option != "animation/optimizer/enabled" && !bool(p_options["animation/optimizer/enabled"])) {
+ if (p_option.begins_with("optimizer/") && p_option != "optimizer/enabled" && !bool(p_options["optimizer/enabled"])) {
+ return false;
+ }
+ if (p_option.begins_with("compression/") && p_option != "compression/enabled" && !bool(p_options["compression/enabled"])) {
return false;
}
- if (p_option.begins_with("animation/slice_")) {
- int max_slice = p_options["animation/slices/amount"];
- int slice = p_option.get_slice("/", 1).get_slice("_", 1).to_int() - 1;
+ if (p_option.begins_with("slice_")) {
+ int max_slice = p_options["slices/amount"];
+ int slice = p_option.get_slice("_", 1).to_int() - 1;
if (slice >= max_slice) {
return false;
}
diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h
index e1e7046be5..a192921966 100644
--- a/editor/import/resource_importer_scene.h
+++ b/editor/import/resource_importer_scene.h
@@ -261,6 +261,7 @@ public:
Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks);
void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all);
void _optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle);
+ void _compress_animations(AnimationPlayer *anim, int p_page_size_kb);
Node *pre_import(const String &p_source_file);
virtual Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp
index 59d0b92ba0..5622d0b145 100644
--- a/editor/inspector_dock.cpp
+++ b/editor/inspector_dock.cpp
@@ -391,7 +391,9 @@ void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Tran
if (!s) {
return;
}
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, p_key);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion());
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale());
}
void InspectorDock::_warning_pressed() {
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 4f9adfff25..5fea8b75d6 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -106,7 +106,6 @@ protected:
void _wip_changed();
void _wip_close();
void _wip_cancel();
- bool _delete_point(const Vector2 &p_gpoint);
void _notification(int p_what);
void _node_removed(Node *p_node);
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
index fe98a91ab3..503e066894 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -109,8 +109,6 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
void _edit_point_pos(double);
void _open_editor();
- void _goto_parent();
-
EditorFileDialog *open_file;
Ref<AnimationNode> file_loaded;
void _file_opened(const String &p_file);
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index eb8db2eaba..26bcff891d 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -187,7 +187,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _scale_changed(const String &p_scale);
void _save_animation(String p_file);
void _load_animations(Vector<String> p_files);
- void _seek_frame_changed(const String &p_frame);
void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false);
void _blend_editor_next_changed(const int p_idx);
@@ -217,7 +216,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _pin_pressed();
- AnimationPlayerEditor();
~AnimationPlayerEditor();
protected:
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 286546f962..5fbf2833b2 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -285,7 +285,6 @@ class EditorAssetLibrary : public PanelContainer {
void _search_text_submitted(const String &p_text = "");
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
- void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
void _filter_debounce_timer_timeout();
void _repository_changed(int p_repository_id);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 8935f715e6..5544d9261e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -3565,7 +3565,7 @@ void CanvasItemEditor::_draw_hover() {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
- Size2 node_name_size = font->get_string_size(node_name);
+ Size2 node_name_size = font->get_string_size(node_name, font_size);
Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
Point2 pos = transform.xform(hovering_results[i].position) - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
@@ -4777,10 +4777,6 @@ void CanvasItemEditor::_popup_callback(int p_op) {
if (key_pos) {
ctrl->set_position(Point2());
}
- /*
- if (key_scale)
- AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
- */
}
}
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 1965efbf30..286771ee08 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -421,8 +421,6 @@ private:
CanvasItem *ref_item;
- void _add_canvas_item(CanvasItem *p_canvas_item);
-
void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false);
void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false);
void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false);
@@ -434,7 +432,6 @@ private:
bool updating_scroll;
void _update_scroll(real_t);
void _update_scrollbars();
- void _append_canvas_item(CanvasItem *p_item);
void _snap_changed();
void _selection_result_pressed(int);
void _selection_menu_hide();
@@ -519,7 +516,6 @@ private:
const Node *p_current);
void _set_anchors_preset(Control::LayoutPreset p_preset);
- void _set_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_to_keep_ratio();
@@ -551,15 +547,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- void end_drag();
- void box_selection_start(Point2 &click);
- bool box_selection_end();
HBoxContainer *get_panel_hb() { return hb; }
- template <class P, class C>
- void space_selected_items();
-
static CanvasItemEditor *singleton;
public:
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 0096152cfe..632207befc 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -252,6 +252,14 @@ void ViewportRotationControl::set_viewport(Node3DEditorViewport *p_viewport) {
viewport = p_viewport;
}
+void Node3DEditorViewport::_view_settings_confirmed(real_t p_interp_delta) {
+ // Set FOV override multiplier back to the default, so that the FOV
+ // setting specified in the View menu is correctly applied.
+ cursor.fov_scale = 1.0;
+
+ _update_camera(p_interp_delta);
+}
+
void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
@@ -318,6 +326,8 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
equal = false;
} else if (!Math::is_equal_approx(old_camera_cursor.distance, camera_cursor.distance, tolerance)) {
equal = false;
+ } else if (!Math::is_equal_approx(old_camera_cursor.fov_scale, camera_cursor.fov_scale, tolerance)) {
+ equal = false;
}
if (!equal || p_interp_delta == 0 || is_orthogonal != orthogonal) {
@@ -383,7 +393,7 @@ float Node3DEditorViewport::get_zfar() const {
}
float Node3DEditorViewport::get_fov() const {
- return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV);
+ return CLAMP(spatial_editor->get_fov() * cursor.fov_scale, MIN_FOV, MAX_FOV);
}
Transform3D Node3DEditorViewport::_get_camera_transform() const {
@@ -479,7 +489,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) {
RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray);
}
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario());
+ Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
Set<Ref<EditorNode3DGizmo>> found_gizmos;
Node *edited_scene = get_tree()->get_edited_scene_root();
@@ -542,7 +552,7 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
Vector3 ray = _get_ray(p_pos);
Vector3 pos = _get_ray_pos(p_pos);
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario());
+ Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
Set<Node3D *> found_nodes;
for (int i = 0; i < instances.size(); i++) {
@@ -1327,17 +1337,25 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
const real_t zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor();
switch (b->get_button_index()) {
case MOUSE_BUTTON_WHEEL_UP: {
- if (is_freelook_active()) {
- scale_freelook_speed(zoom_factor);
+ if (b->is_alt_pressed()) {
+ scale_fov(-0.05);
} else {
- scale_cursor_distance(1.0 / zoom_factor);
+ if (is_freelook_active()) {
+ scale_freelook_speed(zoom_factor);
+ } else {
+ scale_cursor_distance(1.0 / zoom_factor);
+ }
}
} break;
case MOUSE_BUTTON_WHEEL_DOWN: {
- if (is_freelook_active()) {
- scale_freelook_speed(1.0 / zoom_factor);
+ if (b->is_alt_pressed()) {
+ scale_fov(0.05);
} else {
- scale_cursor_distance(zoom_factor);
+ if (is_freelook_active()) {
+ scale_freelook_speed(1.0 / zoom_factor);
+ } else {
+ scale_cursor_distance(zoom_factor);
+ }
}
} break;
case MOUSE_BUTTON_RIGHT: {
@@ -2305,6 +2323,18 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
emit_signal(SNAME("toggle_maximize_view"), this);
}
}
+
+ if (ED_IS_SHORTCUT("spatial_editor/decrease_fov", p_event)) {
+ scale_fov(-0.05);
+ }
+
+ if (ED_IS_SHORTCUT("spatial_editor/increase_fov", p_event)) {
+ scale_fov(0.05);
+ }
+
+ if (ED_IS_SHORTCUT("spatial_editor/reset_fov", p_event)) {
+ reset_fov();
+ }
}
// freelook uses most of the useful shortcuts, like save, so its ok
@@ -2472,6 +2502,16 @@ void Node3DEditorViewport::set_freelook_active(bool active_now) {
freelook_active = active_now;
}
+void Node3DEditorViewport::scale_fov(real_t p_fov_offset) {
+ cursor.fov_scale = CLAMP(cursor.fov_scale + p_fov_offset, 0.1, 2.5);
+ surface->update();
+}
+
+void Node3DEditorViewport::reset_fov() {
+ cursor.fov_scale = 1.0;
+ surface->update();
+}
+
void Node3DEditorViewport::scale_cursor_distance(real_t scale) {
real_t min_distance = MAX(camera->get_near() * 4, ZOOM_FREELOOK_MIN);
real_t max_distance = MIN(camera->get_far() / 4, ZOOM_FREELOOK_MAX);
@@ -3213,7 +3253,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
continue;
}
- undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation());
+ undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_euler_normalized());
undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
}
undo_redo->commit_action();
@@ -7343,6 +7383,9 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M);
ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F);
ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
+ ED_SHORTCUT("spatial_editor/decrease_fov", TTR("Decrease Field of View"), KEY_MASK_CMD + KEY_EQUAL); // Usually direct access key for `KEY_PLUS`.
+ ED_SHORTCUT("spatial_editor/increase_fov", TTR("Increase Field of View"), KEY_MASK_CMD + KEY_MINUS);
+ ED_SHORTCUT("spatial_editor/reset_fov", TTR("Reset Field of View to Default"), KEY_MASK_CMD + KEY_0);
PopupMenu *p;
@@ -7490,7 +7533,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar);
for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) {
- settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_update_camera), varray(0.0));
+ settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_view_settings_confirmed), varray(0.0));
}
/* XFORM DIALOG */
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 2d5aeaa981..e1318f52a8 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -315,7 +315,7 @@ private:
struct Cursor {
Vector3 pos;
- real_t x_rot, y_rot, distance;
+ real_t x_rot, y_rot, distance, fov_scale;
Vector3 eye_pos; // Used in freelook mode
bool region_select;
Point2 region_begin, region_end;
@@ -325,6 +325,7 @@ private:
x_rot = 0.5;
y_rot = -0.5;
distance = 4;
+ fov_scale = 1.0;
region_select = false;
}
};
@@ -333,6 +334,8 @@ private:
Cursor cursor; // Immediate cursor
Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes)
+ void scale_fov(real_t p_fov_offset);
+ void reset_fov();
void scale_cursor_distance(real_t scale);
void set_freelook_active(bool active_now);
@@ -349,6 +352,7 @@ private:
void set_message(String p_message, float p_time = 5);
+ void _view_settings_confirmed(real_t p_interp_delta);
void _update_camera(real_t p_interp_delta);
Transform3D to_camera_transform(const Cursor &p_cursor) const;
void _draw();
@@ -677,8 +681,6 @@ private:
void _register_all_gizmos();
- Node3DEditor();
-
void _selection_changed();
void _refresh_menu_icons();
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 04ab458eb5..943765d4e0 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -59,7 +59,6 @@ class ResourcePreloaderEditor : public PanelContainer {
ResourcePreloader *preloader;
void _load_pressed();
- void _load_scene_pressed();
void _files_load_request(const Vector<String> &p_paths);
void _paste_pressed();
void _remove_resource(const String &p_to_remove);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 8caebc1c8c..2b0bdfd109 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -371,14 +371,11 @@ class ScriptEditor : public PanelContainer {
bool use_space_indentation;
bool convert_indent_on_save;
- void _trim_trailing_whitespace(TextEdit *tx);
-
void _goto_script_line2(int p_line);
void _goto_script_line(REF p_script, int p_line);
void _set_execution(REF p_script, int p_line);
void _clear_execution(REF p_script);
void _breaked(bool p_breaked, bool p_can_debug);
- void _update_window_menu();
void _script_created(Ref<Script> p_script);
void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled);
void _clear_breakpoints();
@@ -430,7 +427,6 @@ class ScriptEditor : public PanelContainer {
void _make_script_list_context_menu();
void _help_search(String p_text);
- void _help_index(String p_text);
void _history_forward();
void _history_back();
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 708eaf2c46..0b8a56503c 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -54,6 +54,7 @@ void BoneTransformEditor::create_editors() {
enabled_checkbox = memnew(EditorPropertyCheck());
enabled_checkbox->set_label("Pose Enabled");
+ enabled_checkbox->set_selectable(false);
enabled_checkbox->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
section->get_vbox()->add_child(enabled_checkbox);
@@ -61,21 +62,27 @@ void BoneTransformEditor::create_editors() {
position_property = memnew(EditorPropertyVector3());
position_property->setup(-10000, 10000, 0.001f, true);
position_property->set_label("Position");
+ position_property->set_selectable(false);
position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ position_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
section->get_vbox()->add_child(position_property);
// Rotation property.
rotation_property = memnew(EditorPropertyQuaternion());
rotation_property->setup(-10000, 10000, 0.001f, true);
rotation_property->set_label("Rotation");
+ rotation_property->set_selectable(false);
rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ rotation_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
section->get_vbox()->add_child(rotation_property);
// Scale property.
scale_property = memnew(EditorPropertyVector3());
scale_property->setup(-10000, 10000, 0.001f, true);
scale_property->set_label("Scale");
+ scale_property->set_selectable(false);
scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ scale_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
section->get_vbox()->add_child(scale_property);
// Transform/Matrix section.
@@ -87,6 +94,7 @@ void BoneTransformEditor::create_editors() {
rest_matrix = memnew(EditorPropertyTransform3D());
rest_matrix->setup(-10000, 10000, 0.001f, true);
rest_matrix->set_label("Transform");
+ rest_matrix->set_selectable(false);
rest_section->get_vbox()->add_child(rest_matrix);
}
@@ -116,6 +124,12 @@ BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
undo_redo = EditorNode::get_undo_redo();
}
+void BoneTransformEditor::set_keyable(const bool p_keyable) {
+ position_property->set_keying(p_keyable);
+ rotation_property->set_keying(p_keyable);
+ scale_property->set_keying(p_keyable);
+}
+
void BoneTransformEditor::set_target(const String &p_prop) {
enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
enabled_checkbox->update_property();
@@ -133,6 +147,23 @@ void BoneTransformEditor::set_target(const String &p_prop) {
rest_matrix->update_property();
}
+void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ PackedStringArray split = p_path.split("/");
+ if (split.size() == 3 && split[0] == "bones") {
+ int bone_idx = split[1].to_int();
+ if (split[2] == "position") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_POSITION_3D, skeleton->get(p_path));
+ }
+ if (split[2] == "rotation") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_ROTATION_3D, skeleton->get(p_path));
+ }
+ if (split[2] == "scale") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_SCALE_3D, skeleton->get(p_path));
+ }
+ }
+}
+
void BoneTransformEditor::_update_properties() {
if (!skeleton) {
return;
@@ -141,30 +172,30 @@ void BoneTransformEditor::_update_properties() {
List<PropertyInfo> props;
skeleton->get_property_list(&props);
for (const PropertyInfo &E : props) {
- PackedStringArray spr = E.name.split("/");
- if (spr.size() == 3 && spr[0] == "bones") {
- if (spr[1].to_int() == selected) {
- if (spr[2] == "enabled") {
+ PackedStringArray split = E.name.split("/");
+ if (split.size() == 3 && split[0] == "bones") {
+ if (split[1].to_int() == selected) {
+ if (split[2] == "enabled") {
enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
enabled_checkbox->update_property();
enabled_checkbox->update();
}
- if (spr[2] == "position") {
+ if (split[2] == "position") {
position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
position_property->update_property();
position_property->update();
}
- if (spr[2] == "rotation") {
+ if (split[2] == "rotation") {
rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
rotation_property->update_property();
rotation_property->update();
}
- if (spr[2] == "scale") {
+ if (split[2] == "scale") {
scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
scale_property->update_property();
scale_property->update();
}
- if (spr[2] == "rest") {
+ if (split[2] == "rest") {
rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
rest_matrix->update_property();
rest_matrix->update();
@@ -178,12 +209,16 @@ Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
void Skeleton3DEditor::set_keyable(const bool p_keyable) {
keyable = p_keyable;
- skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS, !p_keyable);
- skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
+ if (p_keyable) {
+ animation_hb->show();
+ } else {
+ animation_hb->hide();
+ }
};
-void Skeleton3DEditor::set_rest_options_enabled(const bool p_rest_options_enabled) {
- rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
+void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) {
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INIT_SELECTED_POSES, !p_bone_options_enabled);
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled);
};
void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
@@ -192,62 +227,76 @@ void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
}
switch (p_skeleton_option) {
- case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
- create_physical_skeleton();
+ case SKELETON_OPTION_INIT_ALL_POSES: {
+ init_pose(true);
break;
}
- case SKELETON_OPTION_INIT_POSE: {
- init_pose();
+ case SKELETON_OPTION_INIT_SELECTED_POSES: {
+ init_pose(false);
break;
}
- case SKELETON_OPTION_INSERT_KEYS: {
- insert_keys(true);
+ case SKELETON_OPTION_ALL_POSES_TO_RESTS: {
+ pose_to_rest(true);
break;
}
- case SKELETON_OPTION_INSERT_KEYS_EXISTED: {
- insert_keys(false);
+ case SKELETON_OPTION_SELECTED_POSES_TO_RESTS: {
+ pose_to_rest(false);
+ break;
+ }
+ case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
+ create_physical_skeleton();
break;
}
}
}
-void Skeleton3DEditor::_on_click_rest_option(int p_rest_option) {
+void Skeleton3DEditor::init_pose(const bool p_all_bones) {
if (!skeleton) {
return;
}
-
- switch (p_rest_option) {
- case REST_OPTION_POSE_TO_REST: {
- pose_to_rest();
- break;
- }
- }
-}
-
-void Skeleton3DEditor::init_pose() {
const int bone_len = skeleton->get_bone_count();
if (!bone_len) {
return;
}
+
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
- for (int i = 0; i < bone_len; i++) {
- Transform3D rest = skeleton->get_bone_rest(i);
- ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
- ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
- ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
- ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
- ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
- ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
+ if (p_all_bones) {
+ for (int i = 0; i < bone_len; i++) {
+ Transform3D rest = skeleton->get_bone_rest(i);
+ ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
+ ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
+ }
+ } else {
+ // Todo: Do method with multiple bone selection.
+ if (selected_bone == -1) {
+ ur->commit_action();
+ return;
+ }
+ Transform3D rest = skeleton->get_bone_rest(selected_bone);
+ ur->add_do_method(skeleton, "set_bone_pose_position", selected_bone, rest.origin);
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", selected_bone, rest.basis.get_rotation_quaternion());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", selected_bone, rest.basis.get_scale());
+ ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone));
}
ur->commit_action();
}
-void Skeleton3DEditor::insert_keys(bool p_all_bones) {
+void Skeleton3DEditor::insert_keys(const bool p_all_bones) {
if (!skeleton) {
return;
}
+ bool pos_enabled = key_loc_button->is_pressed();
+ bool rot_enabled = key_rot_button->is_pressed();
+ bool scl_enabled = key_scale_button->is_pressed();
+
int bone_len = skeleton->get_bone_count();
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
String path = root->get_path_to(skeleton);
@@ -261,26 +310,44 @@ void Skeleton3DEditor::insert_keys(bool p_all_bones) {
continue;
}
- if (!p_all_bones && !te->has_transform_track(skeleton, name)) {
- continue;
+ if (pos_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_POSITION_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_POSITION_3D, skeleton->get_bone_pose_position(i));
+ }
+ if (rot_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_ROTATION_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_ROTATION_3D, skeleton->get_bone_pose_rotation(i));
+ }
+ if (scl_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_SCALE_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_SCALE_3D, skeleton->get_bone_pose_scale(i));
}
-
- Transform3D tform = skeleton->get_bone_pose(i);
- te->insert_transform_key(skeleton, name, tform);
}
te->commit_insert_queue();
}
-void Skeleton3DEditor::pose_to_rest() {
+void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
if (!skeleton) {
return;
}
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
+ }
- // Todo: Do method with multiple bone selection.
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
- ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
- ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+ if (p_all_bones) {
+ for (int i = 0; i < bone_len; i++) {
+ ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));
+ ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
+ }
+ } else {
+ // Todo: Do method with multiple bone selection.
+ if (selected_bone == -1) {
+ ur->commit_action();
+ return;
+ }
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
+ }
ur->commit_action();
}
@@ -466,11 +533,12 @@ void Skeleton3DEditor::_joint_tree_selection_changed() {
const String bone_path = "bones/" + itos(b_idx) + "/";
pose_editor->set_target(bone_path);
+ pose_editor->set_keyable(keyable);
selected_bone = b_idx;
}
}
pose_editor->set_visible(selected);
- set_rest_options_enabled(selected);
+ set_bone_options_enabled(selected);
_update_properties();
_update_gizmo_visible();
}
@@ -549,43 +617,82 @@ void Skeleton3DEditor::create_editors() {
skeleton_options->set_text(TTR("Skeleton3D"));
skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
- skeleton_options->get_popup()->add_item(TTR("Init pose"), SKELETON_OPTION_INIT_POSE);
- skeleton_options->get_popup()->add_item(TTR("Insert key of all bone poses"), SKELETON_OPTION_INSERT_KEYS);
- skeleton_options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), SKELETON_OPTION_INSERT_KEYS_EXISTED);
- skeleton_options->get_popup()->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
-
- skeleton_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+ // Skeleton options.
+ PopupMenu *p = skeleton_options->get_popup();
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_all_poses", TTR("Init all Poses")), SKELETON_OPTION_INIT_ALL_POSES);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_selected_poses", TTR("Init selected Poses")), SKELETON_OPTION_INIT_SELECTED_POSES);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);
+ p->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
- // Create Rest Option in Top Menu Bar.
- rest_options = memnew(MenuButton);
- ne->add_control_to_menu_panel(rest_options);
-
- rest_options->set_text(TTR("Edit Rest"));
- rest_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")));
-
- rest_options->get_popup()->add_item(TTR("Apply current pose to rest"), REST_OPTION_POSE_TO_REST);
- rest_options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_rest_option));
- set_rest_options_enabled(false);
+ p->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+ set_bone_options_enabled(false);
Vector<Variant> button_binds;
button_binds.resize(1);
edit_mode_button = memnew(Button);
ne->add_control_to_menu_panel(edit_mode_button);
- edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
- edit_mode_button->set_toggle_mode(true);
edit_mode_button->set_flat(true);
+ edit_mode_button->set_toggle_mode(true);
+ edit_mode_button->set_focus_mode(FOCUS_NONE);
+ edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));
edit_mode = false;
- set_keyable(te->has_keying());
-
if (skeleton) {
skeleton->add_child(handles_mesh_instance);
handles_mesh_instance->set_skeleton_path(NodePath(""));
}
+ // Keying buttons.
+ animation_hb = memnew(HBoxContainer);
+ ne->add_control_to_menu_panel(animation_hb);
+ animation_hb->add_child(memnew(VSeparator));
+ animation_hb->hide();
+
+ key_loc_button = memnew(Button);
+ key_loc_button->set_flat(true);
+ key_loc_button->set_toggle_mode(true);
+ key_loc_button->set_pressed(false);
+ key_loc_button->set_focus_mode(FOCUS_NONE);
+ key_loc_button->set_tooltip(TTR("Translation mask for inserting keys."));
+ animation_hb->add_child(key_loc_button);
+
+ key_rot_button = memnew(Button);
+ key_rot_button->set_flat(true);
+ key_rot_button->set_toggle_mode(true);
+ key_rot_button->set_pressed(true);
+ key_rot_button->set_focus_mode(FOCUS_NONE);
+ key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys."));
+ animation_hb->add_child(key_rot_button);
+
+ key_scale_button = memnew(Button);
+ key_scale_button->set_flat(true);
+ key_scale_button->set_toggle_mode(true);
+ key_scale_button->set_pressed(false);
+ key_scale_button->set_focus_mode(FOCUS_NONE);
+ key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
+ animation_hb->add_child(key_scale_button);
+
+ key_insert_button = memnew(Button);
+ key_insert_button->set_flat(true);
+ key_insert_button->set_focus_mode(FOCUS_NONE);
+ key_insert_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(false));
+ key_insert_button->set_tooltip(TTR("Insert key of bone poses already exist track."));
+ key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTR("Insert Key (Existing Tracks)"), KEY_INSERT));
+ animation_hb->add_child(key_insert_button);
+
+ key_insert_all_button = memnew(Button);
+ key_insert_all_button->set_flat(true);
+ key_insert_all_button->set_focus_mode(FOCUS_NONE);
+ key_insert_all_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(true));
+ key_insert_all_button->set_tooltip(TTR("Insert key of all bone poses."));
+ key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTR("Insert Key (All Bones)"), KEY_MASK_CMD + KEY_INSERT));
+ animation_hb->add_child(key_insert_all_button);
+
+ // Bone tree.
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
EditorInspectorSection *bones_section = memnew(EditorInspectorSection);
@@ -613,12 +720,19 @@ void Skeleton3DEditor::create_editors() {
pose_editor->set_label(TTR("Bone Transform"));
pose_editor->set_visible(false);
add_child(pose_editor);
+
+ set_keyable(te->has_keying());
}
void Skeleton3DEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
- edit_mode_button->set_icon(get_theme_icon("ToolBoneSelect", "EditorIcons"));
+ edit_mode_button->set_icon(get_theme_icon(SNAME("ToolBoneSelect"), SNAME("EditorIcons")));
+ key_loc_button->set_icon(get_theme_icon(SNAME("KeyPosition"), SNAME("EditorIcons")));
+ key_rot_button->set_icon(get_theme_icon(SNAME("KeyRotation"), SNAME("EditorIcons")));
+ key_scale_button->set_icon(get_theme_icon(SNAME("KeyScale"), SNAME("EditorIcons")));
+ key_insert_button->set_icon(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")));
+ key_insert_all_button->set_icon(get_theme_icon(SNAME("NewKey"), SNAME("EditorIcons")));
get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
break;
}
@@ -643,7 +757,6 @@ void Skeleton3DEditor::_node_removed(Node *p_node) {
if (skeleton && p_node == skeleton) {
skeleton = nullptr;
skeleton_options->hide();
- rest_options->hide();
}
_update_properties();
@@ -655,7 +768,6 @@ void Skeleton3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select);
ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties);
ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
- ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &Skeleton3DEditor::_on_click_rest_option);
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
@@ -866,6 +978,11 @@ Skeleton3DEditor::~Skeleton3DEditor() {
Node3DEditor *ne = Node3DEditor::get_singleton();
+ if (animation_hb) {
+ ne->remove_control_from_menu_panel(animation_hb);
+ memdelete(animation_hb);
+ }
+
if (separator) {
ne->remove_control_from_menu_panel(separator);
memdelete(separator);
@@ -876,11 +993,6 @@ Skeleton3DEditor::~Skeleton3DEditor() {
memdelete(skeleton_options);
}
- if (rest_options) {
- ne->remove_control_from_menu_panel(rest_options);
- memdelete(rest_options);
- }
-
if (edit_mode_button) {
ne->remove_control_from_menu_panel(edit_mode_button);
memdelete(edit_mode_button);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 3b4dd362fb..1dd2d2281d 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -45,7 +45,6 @@ class Joint;
class PhysicalBone3D;
class Skeleton3DEditorPlugin;
class Button;
-class CheckBox;
class BoneTransformEditor : public VBoxContainer {
GDCLASS(BoneTransformEditor, VBoxContainer);
@@ -67,9 +66,6 @@ class BoneTransformEditor : public VBoxContainer {
UndoRedo *undo_redo;
- // Button *key_button = nullptr;
-
- bool keyable = false;
bool toggle_enabled = false;
bool updating = false;
@@ -79,6 +75,8 @@ class BoneTransformEditor : public VBoxContainer {
void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
+ void _property_keyed(const String &p_path, bool p_advance);
+
protected:
void _notification(int p_what);
@@ -88,6 +86,7 @@ public:
// Which transform target to modify.
void set_target(const String &p_prop);
void set_label(const String &p_label) { label = p_label; }
+ void set_keyable(const bool p_keyable);
void _update_properties();
};
@@ -98,14 +97,11 @@ class Skeleton3DEditor : public VBoxContainer {
friend class Skeleton3DEditorPlugin;
enum SkeletonOption {
- SKELETON_OPTION_INIT_POSE,
- SKELETON_OPTION_INSERT_KEYS,
- SKELETON_OPTION_INSERT_KEYS_EXISTED,
- SKELETON_OPTION_CREATE_PHYSICAL_SKELETON
- };
-
- enum RestOption {
- REST_OPTION_POSE_TO_REST
+ SKELETON_OPTION_INIT_ALL_POSES,
+ SKELETON_OPTION_INIT_SELECTED_POSES,
+ SKELETON_OPTION_ALL_POSES_TO_RESTS,
+ SKELETON_OPTION_SELECTED_POSES_TO_RESTS,
+ SKELETON_OPTION_CREATE_PHYSICAL_SKELETON,
};
struct BoneInfo {
@@ -124,11 +120,17 @@ class Skeleton3DEditor : public VBoxContainer {
VSeparator *separator;
MenuButton *skeleton_options = nullptr;
- MenuButton *rest_options = nullptr;
Button *edit_mode_button;
bool edit_mode = false;
+ HBoxContainer *animation_hb;
+ Button *key_loc_button;
+ Button *key_rot_button;
+ Button *key_scale_button;
+ Button *key_insert_button;
+ Button *key_insert_all_button;
+
EditorFileDialog *file_dialog = nullptr;
bool keyable;
@@ -136,7 +138,6 @@ class Skeleton3DEditor : public VBoxContainer {
static Skeleton3DEditor *singleton;
void _on_click_skeleton_option(int p_skeleton_option);
- void _on_click_rest_option(int p_rest_option);
void _file_selected(const String &p_file);
TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
void edit_mode_toggled(const bool pressed);
@@ -148,9 +149,10 @@ class Skeleton3DEditor : public VBoxContainer {
void create_editors();
- void init_pose();
- void insert_keys(bool p_all_bones);
- void pose_to_rest();
+ void init_pose(const bool p_all_bones);
+ void pose_to_rest(const bool p_all_bones);
+
+ void insert_keys(const bool p_all_bones);
void create_physical_skeleton();
PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
@@ -160,7 +162,7 @@ class Skeleton3DEditor : public VBoxContainer {
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void set_keyable(const bool p_keyable);
- void set_rest_options_enabled(const bool p_rest_options_enabled);
+ void set_bone_options_enabled(const bool p_bone_options_enabled);
// Handle.
MeshInstance3D *handles_mesh_instance;
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index 5e3b2fb8c1..9732384000 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -100,7 +100,6 @@ class SpriteFramesEditor : public HSplitContainer {
float min_sheet_zoom;
void _load_pressed();
- void _load_scene_pressed();
void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
void _copy_pressed();
void _paste_pressed();
@@ -128,7 +127,6 @@ class SpriteFramesEditor : public HSplitContainer {
UndoRedo *undo_redo;
- bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const;
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 60f9e09536..b6becbb1c7 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -347,7 +347,6 @@ class ThemeTypeEditor : public MarginContainer {
void _update_type_items();
void _list_type_selected(int p_index);
- void _select_type(String p_type_name);
void _add_type_button_cbk();
void _add_default_type_items();
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index d26d0ec39d..e13a10fe3f 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -117,7 +117,7 @@ void ThemeEditorPreview::_draw_picker_overlay() {
}
Rect2 highlight_label_rect = highlight_rect;
- highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name);
+ highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name, theme_cache.font_size);
int margin_top = theme_cache.preview_picker_label->get_margin(SIDE_TOP);
int margin_left = theme_cache.preview_picker_label->get_margin(SIDE_LEFT);
@@ -133,7 +133,7 @@ void ThemeEditorPreview::_draw_picker_overlay() {
Point2 label_pos = highlight_label_rect.position;
label_pos.y += highlight_label_rect.size.y - margin_bottom;
label_pos.x += margin_left;
- picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name);
+ picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name, HALIGN_LEFT, -1, theme_cache.font_size);
}
}
@@ -188,6 +188,7 @@ void ThemeEditorPreview::_notification(int p_what) {
theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor"));
theme_cache.preview_picker_label = get_theme_stylebox(SNAME("preview_picker_label"), SNAME("ThemeEditor"));
theme_cache.preview_picker_font = get_theme_font(SNAME("status_source"), SNAME("EditorFonts"));
+ theme_cache.font_size = get_theme_font_size(SNAME("font_size"), SNAME("EditorFonts"));
} break;
case NOTIFICATION_PROCESS: {
time_left -= get_process_delta_time();
diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h
index 4e1b149e70..f973119257 100644
--- a/editor/plugins/theme_editor_preview.h
+++ b/editor/plugins/theme_editor_preview.h
@@ -65,6 +65,7 @@ class ThemeEditorPreview : public VBoxContainer {
Color preview_picker_overlay_color;
Ref<StyleBox> preview_picker_label;
Ref<Font> preview_picker_font;
+ int font_size = 16;
} theme_cache;
double time_left = 0;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index 104f46f773..e9d80bb4b8 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -987,6 +987,7 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
p_canvas_item->draw_rect(rect, value);
} else {
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
String text;
switch (value.get_type()) {
case Variant::INT:
@@ -1018,8 +1019,8 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
}
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
@@ -1582,6 +1583,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
// Dim terrains with wrong terrain set.
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_id(i);
if (coords != hovered_coords) {
@@ -1604,8 +1606,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
} else {
text = "-";
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
}
@@ -1755,6 +1757,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
// Dim terrains with wrong terrain set.
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_id(i);
for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) {
@@ -1779,8 +1782,8 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
} else {
text = "-";
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 5dfa44a353..21ef94b999 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -2219,569 +2219,7 @@ Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() con
return tabs;
}
-Map<Vector2i, TileSet::CellNeighbor> TileMapEditorTerrainsPlugin::Constraint::get_overlapping_coords_and_peering_bits() const {
- Map<Vector2i, TileSet::CellNeighbor> output;
- Ref<TileSet> tile_set = tile_map->get_tileset();
- ERR_FAIL_COND_V(!tile_set.is_valid(), output);
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- // Half offset shapes.
- TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- }
- }
- return output;
-}
-
-TileMapEditorTerrainsPlugin::Constraint::Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
- // The way we build the constraint make it easy to detect conflicting constraints.
- tile_map = p_tile_map;
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE || shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- // Half-offset shapes
- TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- }
- }
- terrain = p_terrain;
-}
-
-Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<Constraint> p_constraints) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Set<TerrainsTilePattern>();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Set<TerrainsTilePattern>();
- }
-
- // Returns all tiles compatible with the given constraints.
- Set<TerrainsTilePattern> compatible_terrain_tile_patterns;
- for (const KeyValue<TerrainsTilePattern, Set<TileMapCell>> &E : per_terrain_terrains_tile_patterns_tiles[p_terrain_set]) {
- int valid = true;
- int in_pattern_count = 0;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
- // Check if the bit is compatible with the constraints.
- Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E.key[in_pattern_count]);
-
- Set<Constraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
- if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
- valid = false;
- break;
- }
- in_pattern_count++;
- }
- }
-
- if (valid) {
- compatible_terrain_tile_patterns.insert(E.key);
- }
- }
-
- return compatible_terrain_tile_patterns;
-}
-
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Set<Constraint>();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Set<Constraint>();
- }
-
- ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>());
- ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>());
-
- // Build a set of dummy constraints get the constrained points.
- Set<Constraint> dummy_constraints;
- for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
- dummy_constraints.insert(Constraint(tile_map, E->get(), bit, -1));
- }
- }
- }
-
- // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
- Set<Constraint> constraints;
- for (Set<Constraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
- Constraint c = E->get();
-
- Map<int, int> terrain_count;
-
- // Count the number of occurrences per terrain.
- Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
- for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
- if (!p_to_replace.has(E_overlapping.key)) {
- TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping.key);
- TileData *neighbor_tile_data = nullptr;
- if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
- neighbor_tile_data = terrain_tiles[neighbor_cell];
- }
-
- int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
- if (terrain_count.has(terrain)) {
- terrain_count[terrain] = 0;
- }
- terrain_count[terrain] += 1;
- }
- }
-
- // Get the terrain with the max number of occurrences.
- int max = 0;
- int max_terrain = -1;
- for (const KeyValue<int, int> &E_terrain_count : terrain_count) {
- if (E_terrain_count.value > max) {
- max = E_terrain_count.value;
- max_terrain = E_terrain_count.key;
- }
- }
-
- // Set the adequate terrain.
- if (max > 0) {
- c.set_terrain(max_terrain);
- constraints.insert(c);
- }
- }
-
- return constraints;
-}
-
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Set<TileMapEditorTerrainsPlugin::Constraint>();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Set<TileMapEditorTerrainsPlugin::Constraint>();
- }
-
- // Compute the constraints needed from the surrounding tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> output;
- int in_pattern_count = 0;
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
- Constraint c = Constraint(tile_map, p_position, side, p_terrains_tile_pattern[in_pattern_count]);
- output.insert(c);
- in_pattern_count++;
- }
- }
-
- return output;
-}
-
-Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Map<Vector2i, TerrainsTilePattern>();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern>();
- }
-
- // Copy the constraints set.
- Set<TileMapEditorTerrainsPlugin::Constraint> constraints = p_constraints;
-
- // Compute all acceptable tiles for each cell.
- Map<Vector2i, Set<TerrainsTilePattern>> per_cell_acceptable_tiles;
- for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
- per_cell_acceptable_tiles[E->get()] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, E->get(), constraints);
- }
-
- // Output map.
- Map<Vector2i, TerrainsTilePattern> output;
-
- // Add all positions to a set.
- Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
- while (!to_replace.is_empty()) {
- // Compute the minimum number of tile possibilities for each cell.
- int min_nb_possibilities = 100000000;
- for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
- min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
- }
-
- // Get the set of possible cells to fill.
- LocalVector<Vector2i> to_choose_from;
- for (const KeyValue<Vector2i, Set<TerrainsTilePattern>> &E : per_cell_acceptable_tiles) {
- if (E.value.size() == min_nb_possibilities) {
- to_choose_from.push_back(E.key);
- }
- }
-
- // Randomly pick a tile out of the most constrained.
- Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
-
- // Randomly select a tile out of them the put it in the grid.
- Set<TerrainsTilePattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
- if (valid_tiles.is_empty()) {
- // No possibilities :/
- break;
- }
- int random_terrain_tile_pattern_index = Math::random(0, valid_tiles.size() - 1);
- Set<TerrainsTilePattern>::Element *E = valid_tiles.front();
- for (int i = 0; i < random_terrain_tile_pattern_index; i++) {
- E = E->next();
- }
- TerrainsTilePattern selected_terrain_tile_pattern = E->get();
-
- // Set the selected cell into the output.
- output[selected_cell_to_replace] = selected_terrain_tile_pattern;
- to_replace.erase(selected_cell_to_replace);
- per_cell_acceptable_tiles.erase(selected_cell_to_replace);
-
- // Add the new constraints from the added tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> new_constraints = _get_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
- constraints.insert(E_constraint->get());
- }
-
- // Compute valid tiles again for neighbors.
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_map->is_existing_neighbor(side)) {
- Vector2i neighbor = tile_map->get_neighbor_cell(selected_cell_to_replace, side);
- if (to_replace.has(neighbor)) {
- per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, neighbor, constraints);
- }
- }
- }
- }
- return output;
-}
-
-TileMapCell TileMapEditorTerrainsPlugin::_get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return TileMapCell();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return TileMapCell();
- }
-
- // Count the sum of probabilities.
- double sum = 0.0;
- Set<TileMapCell> set = per_terrain_terrains_tile_patterns_tiles[p_terrain_set][p_terrain_tile_pattern];
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
- if (E->get().source_id >= 0) {
- Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
-
- Ref<TileSetAtlasSource> atlas_source = source;
- if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
- sum += tile_data->get_probability();
- } else {
- sum += 1.0;
- }
- } else {
- sum += 1.0;
- }
- }
-
- // Generate a random number.
- double count = 0.0;
- double picked = Math::random(0.0, sum);
-
- // Pick the tile.
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
- if (E->get().source_id >= 0) {
- Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
-
- Ref<TileSetAtlasSource> atlas_source = source;
- if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
- count += tile_data->get_probability();
- } else {
- count += 1.0;
- }
- } else {
- count += 1.0;
- }
-
- if (count >= picked) {
- return E->get();
- }
- }
-
- ERR_FAIL_V(TileMapCell());
-}
-
-Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const {
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -2795,20 +2233,20 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
Map<Vector2i, TileMapCell> output;
// Add the constraints from the added tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> added_tiles_constraints_set;
- for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ Set<TileMap::TerrainConstraint> added_tiles_constraints_set;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
Vector2i coords = E_to_paint.key;
- TerrainsTilePattern terrains_tile_pattern = E_to_paint.value;
+ TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
- Set<TileMapEditorTerrainsPlugin::Constraint> cell_constraints = _get_constraints_from_added_tile(coords, p_terrain_set, terrains_tile_pattern);
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
+ Set<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
+ for (Set<TileMap::TerrainConstraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
added_tiles_constraints_set.insert(E->get());
}
}
// Build the list of potential tiles to replace.
Set<Vector2i> potential_to_replace;
- for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
Vector2i coords = E_to_paint.key;
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
@@ -2823,42 +2261,42 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
// Set of tiles to replace
Set<Vector2i> to_replace;
- // Add the central tiles to the one to replace. TODO: maybe change that.
- for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
+ // Add the central tiles to the one to replace.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
to_replace.insert(E_to_paint.key);
}
// Add the constraints from the surroundings of the modified areas.
- Set<TileMapEditorTerrainsPlugin::Constraint> removed_cells_constraints_set;
+ Set<TileMap::TerrainConstraint> removed_cells_constraints_set;
bool to_replace_modified = true;
while (to_replace_modified) {
// Get the constraints from the removed cells.
- removed_cells_constraints_set = _get_constraints_from_removed_cells_list(to_replace, p_terrain_set);
+ removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set);
// Filter the sources to make sure they are in the potential_to_replace.
- Map<Constraint, Set<Vector2i>> source_tiles_of_constraint;
- for (Set<Constraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
+ Map<TileMap::TerrainConstraint, Set<Vector2i>> per_constraint_tiles;
+ for (Set<TileMap::TerrainConstraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
- source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint.key);
+ per_constraint_tiles[E->get()].insert(E_source_tile_of_constraint.key);
}
}
}
to_replace_modified = false;
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
- Constraint c = E->get();
+ for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ TileMap::TerrainConstraint c = E->get();
// Check if we have a conflict in constraints.
if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
// If we do, we search for a neighbor to remove.
- if (source_tiles_of_constraint.has(c) && !source_tiles_of_constraint[c].is_empty()) {
+ if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
// Remove it.
- Vector2i to_add_to_remove = source_tiles_of_constraint[c].front()->get();
+ Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
potential_to_replace.erase(to_add_to_remove);
to_replace.insert(to_add_to_remove);
to_replace_modified = true;
- for (KeyValue<Constraint, Set<Vector2i>> &E_source_tiles_of_constraint : source_tiles_of_constraint) {
+ for (KeyValue<TileMap::TerrainConstraint, Set<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
E_source_tiles_of_constraint.value.erase(to_add_to_remove);
}
break;
@@ -2868,22 +2306,27 @@ Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map
}
// Combine all constraints together.
- Set<TileMapEditorTerrainsPlugin::Constraint> constraints = removed_cells_constraints_set;
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ Set<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
+ for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
constraints.insert(E->get());
}
+ // Remove the central tiles from the ones to replace.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ to_replace.erase(E_to_paint.key);
+ }
+
// Run WFC to fill the holes with the constraints.
- Map<Vector2i, TerrainsTilePattern> wfc_output = _wave_function_collapse(to_replace, p_terrain_set, constraints);
+ Map<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
- // Use the WFC run for the output.
- for (const KeyValue<Vector2i, TerrainsTilePattern> &E : wfc_output) {
- output[E.key] = _get_random_tile_from_pattern(p_terrain_set, E.value);
+ // Actually paint the tiles.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ output[E_to_paint.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
}
- // Override the WFC results to make sure at least the painted tiles are actually painted.
- for (const KeyValue<Vector2i, TerrainsTilePattern> &E_to_paint : p_to_paint) {
- output[E_to_paint.key] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint.value);
+ // Use the WFC run for the output.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
+ output[E.key] = tile_set->get_random_tile_from_pattern(p_terrain_set, E.value);
}
return output;
@@ -2901,10 +2344,23 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
switch (drag_type) {
case DRAG_TYPE_PICK: {
Vector2i coords = tile_map->world_to_map(mpos);
- TileMapCell tile = tile_map->get_cell(tile_map_layer, coords);
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, coords);
- if (terrain_tiles.has(tile)) {
- Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ TileData *tile_data = nullptr;
+
+ Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
+ }
+
+ if (tile_data) {
+ Array terrains_pattern = tile_data->get_terrains_pattern();
// Find the tree item for the right terrain set.
bool need_tree_item_switch = true;
@@ -2914,7 +2370,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
int terrain_set = metadata_dict["terrain_set"];
int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
need_tree_item_switch = false;
}
}
@@ -2926,7 +2382,7 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
int terrain_set = metadata_dict["terrain_set"];
int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
// Found
tree_item->select(0);
_update_tiles_list();
@@ -2940,10 +2396,10 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (tree_item) {
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+ TileSet::TerrainsPattern in_meta_terrains_pattern = metadata_dict["terrains_pattern"];
bool equals = true;
- for (int j = 0; j < terrains_tile_pattern.size(); j++) {
- if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
+ for (int j = 0; j < terrains_pattern.size(); j++) {
+ if (terrains_pattern[j] != in_meta_terrains_pattern[j]) {
equals = false;
break;
}
@@ -2999,7 +2455,7 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
// Get the selected terrain.
- TerrainsTilePattern selected_terrains_tile_pattern;
+ TileSet::TerrainsPattern selected_terrains_pattern;
int selected_terrain_set = -1;
TreeItem *selected_tree_item = terrains_tree->get_selected();
@@ -3010,16 +2466,16 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
// Selected tile
if (erase_button->is_pressed()) {
- selected_terrains_tile_pattern.clear();
+ selected_terrains_pattern.clear();
for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, side)) {
- selected_terrains_tile_pattern.push_back(-1);
+ selected_terrains_pattern.push_back(-1);
}
}
} else if (terrains_tile_list->is_anything_selected()) {
metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
- selected_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
+ selected_terrains_pattern = metadata_dict["terrains_pattern"];
}
}
@@ -3032,9 +2488,9 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
case DRAG_TYPE_PAINT: {
if (selected_terrain_set >= 0) {
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
- Map<Vector2i, TerrainsTilePattern> to_draw;
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
for (int i = 0; i < line.size(); i++) {
- to_draw[line[i]] = selected_terrains_tile_pattern;
+ to_draw[line[i]] = selected_terrains_pattern;
}
Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
for (const KeyValue<Vector2i, TileMapCell> &E : modified) {
@@ -3066,14 +2522,14 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
drag_type = DRAG_TYPE_PICK;
} else {
// Paint otherwise.
- if (selected_terrain_set >= 0 && !selected_terrains_tile_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ if (selected_terrain_set >= 0 && !selected_terrains_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- Map<Vector2i, TerrainsTilePattern> terrains_to_draw;
- terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_tile_pattern;
+ Map<Vector2i, TileSet::TerrainsPattern> terrains_to_draw;
+ terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_pattern;
Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
@@ -3097,26 +2553,6 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
return false;
}
-TileMapEditorTerrainsPlugin::TerrainsTilePattern TileMapEditorTerrainsPlugin::_build_terrains_tile_pattern(TileData *p_tile_data) {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return TerrainsTilePattern();
- }
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return TerrainsTilePattern();
- }
-
- TerrainsTilePattern output;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- if (tile_set->is_valid_peering_bit_terrain(p_tile_data->get_terrain_set(), TileSet::CellNeighbor(i))) {
- output.push_back(p_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(i)));
- }
- }
- return output;
-}
-
void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -3128,45 +2564,12 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
return;
}
- // Compute the tile sides.
- tile_sides.clear();
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- } else {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- } else {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- }
- }
-
// Organizes tiles into structures.
- per_terrain_terrains_tile_patterns_tiles.resize(tile_set->get_terrain_sets_count());
- per_terrain_terrains_tile_patterns.resize(tile_set->get_terrain_sets_count());
+ per_terrain_terrains_patterns.resize(tile_set->get_terrain_sets_count());
for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
- per_terrain_terrains_tile_patterns_tiles[i].clear();
- per_terrain_terrains_tile_patterns[i].resize(tile_set->get_terrains_count(i));
- for (int j = 0; j < (int)per_terrain_terrains_tile_patterns[i].size(); j++) {
- per_terrain_terrains_tile_patterns[i][j].clear();
+ per_terrain_terrains_patterns[i].resize(tile_set->get_terrains_count(i));
+ for (int j = 0; j < (int)per_terrain_terrains_patterns[i].size(); j++) {
+ per_terrain_terrains_patterns[i][j].clear();
}
}
@@ -3184,22 +2587,20 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
- ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_tile_patterns.size());
+ ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_patterns.size());
TileMapCell cell;
cell.source_id = source_id;
cell.set_atlas_coords(tile_id);
cell.alternative_tile = alternative_id;
- TerrainsTilePattern terrains_tile_pattern = _build_terrains_tile_pattern(tile_data);
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Terrain bits.
- for (int i = 0; i < terrains_tile_pattern.size(); i++) {
- int terrain = terrains_tile_pattern[i];
- if (terrain >= 0 && terrain < (int)per_terrain_terrains_tile_patterns[terrain_set].size()) {
- per_terrain_terrains_tile_patterns[terrain_set][terrain].insert(terrains_tile_pattern);
- terrain_tiles[cell] = tile_data;
- per_terrain_terrains_tile_patterns_tiles[terrain_set][terrains_tile_pattern].insert(cell);
+ for (int i = 0; i < terrains_pattern.size(); i++) {
+ int terrain = terrains_pattern[i];
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+ per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
}
}
}
@@ -3207,22 +2608,6 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
}
}
}
-
- // Add the empty cell in the possible patterns and cells.
- for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
- TerrainsTilePattern empty_pattern;
- for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
- if (tile_set->is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
- empty_pattern.push_back(-1);
- }
- }
-
- TileMapCell empty_cell;
- empty_cell.source_id = TileSet::INVALID_SOURCE;
- empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
- empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
- per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell);
- }
}
void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
@@ -3291,12 +2676,13 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
int selected_terrain_set = metadata_dict["terrain_set"];
int selected_terrain_id = metadata_dict["terrain_id"];
- ERR_FAIL_INDEX(selected_terrain_set, (int)per_terrain_terrains_tile_patterns.size());
- ERR_FAIL_INDEX(selected_terrain_id, (int)per_terrain_terrains_tile_patterns[selected_terrain_set].size());
+ ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
+ ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
// Sort the items in a map by the number of corresponding terrains.
- Map<int, Set<TerrainsTilePattern>> sorted;
- for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
+ Map<int, Set<TileSet::TerrainsPattern>> sorted;
+
+ for (Set<TileSet::TerrainsPattern>::Element *E = per_terrain_terrains_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
// Count the number of matching sides/terrains.
int count = 0;
@@ -3308,9 +2694,9 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
sorted[count].insert(E->get());
}
- for (Map<int, Set<TerrainsTilePattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
- for (Set<TerrainsTilePattern>::Element *E = E_set->get().front(); E; E = E->next()) {
- TerrainsTilePattern terrains_tile_pattern = E->get();
+ for (Map<int, Set<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
+ for (Set<TileSet::TerrainsPattern>::Element *E = E_set->get().front(); E; E = E->next()) {
+ TileSet::TerrainsPattern terrains_pattern = E->get();
// Get the icon.
Ref<Texture2D> icon;
@@ -3318,15 +2704,15 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
bool transpose = false;
double max_probability = -1.0;
- for (Set<TileMapCell>::Element *E_tile_map_cell = per_terrain_terrains_tile_patterns_tiles[selected_terrain_set][terrains_tile_pattern].front(); E_tile_map_cell; E_tile_map_cell = E_tile_map_cell->next()) {
- Ref<TileSetSource> source = tile_set->get_source(E_tile_map_cell->get().source_id);
+ for (const TileMapCell &cell : tile_set->get_tiles_for_terrains_pattern(selected_terrain_set, terrains_pattern)) {
+ Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E_tile_map_cell->get().get_atlas_coords(), E_tile_map_cell->get().alternative_tile));
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
if (tile_data->get_probability() > max_probability) {
icon = atlas_source->get_texture();
- region = atlas_source->get_tile_texture_region(E_tile_map_cell->get().get_atlas_coords());
+ region = atlas_source->get_tile_texture_region(cell.get_atlas_coords());
if (tile_data->get_flip_h()) {
region.position.x += region.size.x;
region.size.x = -region.size.x;
@@ -3347,7 +2733,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
terrains_tile_list->set_item_icon_region(item_index, region);
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
Dictionary list_metadata_dict;
- list_metadata_dict["terrains_tile_pattern"] = terrains_tile_pattern;
+ list_metadata_dict["terrains_pattern"] = terrains_pattern;
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
}
}
@@ -3508,7 +2894,11 @@ void TileMapEditor::_layers_selection_button_draw() {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (layers_selection_button->has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 1f1a560113..0513a7b365 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -243,66 +243,16 @@ private:
Map<Vector2i, TileMapCell> drag_modified;
// Painting
- class Constraint {
- private:
- const TileMap *tile_map;
- Vector2i base_cell_coords = Vector2i();
- int bit = -1;
- int terrain = -1;
-
- public:
- // TODO implement difference operator.
- bool operator<(const Constraint &p_other) const {
- if (base_cell_coords == p_other.base_cell_coords) {
- return bit < p_other.bit;
- }
- return base_cell_coords < p_other.base_cell_coords;
- }
-
- String to_string() const {
- return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
- }
-
- Vector2i get_base_cell_coords() const {
- return base_cell_coords;
- }
-
- Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
-
- void set_terrain(int p_terrain) {
- terrain = p_terrain;
- }
-
- int get_terrain() const {
- return terrain;
- }
-
- Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
- Constraint() {}
- };
-
- typedef Array TerrainsTilePattern;
-
- Set<TerrainsTilePattern> _get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
- Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const;
- Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const;
- Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
- TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const;
- Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const;
+ Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
void _stop_dragging();
- // Cached data.
- TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data);
- LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles;
- LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns;
-
- Map<TileMapCell, TileData *> terrain_tiles;
- LocalVector<TileSet::CellNeighbor> tile_sides;
-
// Bottom panel.
Tree *terrains_tree;
ItemList *terrains_tile_list;
+ // Cache.
+ LocalVector<LocalVector<Set<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
+
// Update functions.
void _update_terrains_cache();
void _update_terrains_tree();
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index c8892bceaa..ae744f697b 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -841,7 +841,11 @@ void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw() {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (tile_data_editor_dropdown_button->has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 0a067dfc17..09ca1f7608 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "visual_shader_editor_plugin.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
@@ -212,19 +213,27 @@ void VisualShaderGraphPlugin::set_uniform_name(VisualShader::Type p_type, int p_
void VisualShaderGraphPlugin::update_curve(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0]) {
- if (((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture()->get_curve());
+ Ref<VisualShaderNodeCurveTexture> tex = Object::cast_to<VisualShaderNodeCurveTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
void VisualShaderGraphPlugin::update_curve_xyz(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0] && links[p_node_id].curve_editors[1] && links[p_node_id].curve_editors[2]) {
- if (((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_x());
- links[p_node_id].curve_editors[1]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_y());
- links[p_node_id].curve_editors[2]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_z());
+ Ref<VisualShaderNodeCurveXYZTexture> tex = Object::cast_to<VisualShaderNodeCurveXYZTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve_x());
+ links[p_node_id].curve_editors[1]->set_curve(tex->get_texture()->get_curve_y());
+ links[p_node_id].curve_editors[2]->set_curve(tex->get_texture()->get_curve_z());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 9f24c5af72..5788959a77 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -335,8 +335,6 @@ class VisualShaderEditor : public VBoxContainer {
void _delete_node_request(int p_type, int p_node);
void _delete_nodes_request();
- void _removed_from_graph();
-
void _node_changed(int p_id);
void _edit_port_default_input(Object *p_button, int p_node, int p_port);
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index eb6c300d5b..f90db02c46 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -100,8 +100,6 @@ class ProjectSettingsEditor : public AcceptDialog {
void _action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before);
void _update_action_map_editor();
- ProjectSettingsEditor();
-
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp
index fc3abbb87e..f0ec78bde6 100644
--- a/editor/quick_open.cpp
+++ b/editor/quick_open.cpp
@@ -55,16 +55,23 @@ void EditorQuickOpen::_build_search_cache(EditorFileSystemDirectory *p_efsd) {
_build_search_cache(p_efsd->get_subdir(i));
}
+ Vector<String> base_types = String(base_type).split(String(","));
for (int i = 0; i < p_efsd->get_file_count(); i++) {
String file_type = p_efsd->get_file_type(i);
- if (ClassDB::is_parent_class(file_type, base_type)) {
- String file = p_efsd->get_file_path(i);
- files.push_back(file.substr(6, file.length()));
-
- // Store refs to used icons.
- String ext = file.get_extension();
- if (!icons.has(ext)) {
- icons.insert(ext, get_theme_icon((has_theme_icon(file_type, SNAME("EditorIcons")) ? file_type : String("Object")), SNAME("EditorIcons")));
+ // Iterate all possible base types.
+ for (String &parent_type : base_types) {
+ if (ClassDB::is_parent_class(file_type, parent_type)) {
+ String file = p_efsd->get_file_path(i);
+ files.push_back(file.substr(6, file.length()));
+
+ // Store refs to used icons.
+ String ext = file.get_extension();
+ if (!icons.has(ext)) {
+ icons.insert(ext, get_theme_icon((has_theme_icon(file_type, SNAME("EditorIcons")) ? file_type : String("Object")), SNAME("EditorIcons")));
+ }
+
+ // Stop testing base types as soon as we got a match.
+ break;
}
}
}
diff --git a/editor/reparent_dialog.h b/editor/reparent_dialog.h
index 5c3a65a522..3fcdda7bed 100644
--- a/editor/reparent_dialog.h
+++ b/editor/reparent_dialog.h
@@ -46,7 +46,6 @@ class ReparentDialog : public ConfirmationDialog {
SceneTreeEditor *tree;
CheckBox *keep_transform;
- void update_tree();
void _reparent();
void _cancel();
@@ -56,7 +55,6 @@ protected:
public:
void set_current(const Set<Node *> &p_selection);
- String get_selected_type();
ReparentDialog();
~ReparentDialog();
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 255e026887..66000602fa 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -246,8 +246,6 @@ class SceneTreeDock : public VBoxContainer {
void _perform_instantiate_scenes(const Vector<String> &p_files, Node *parent, int p_pos);
void _replace_with_branch_scene(const String &p_file, Node *base);
- void _file_selected(String p_file);
-
void _remote_tree_selected();
void _local_tree_selected();
diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h
index 4acd5d8486..b4c40ab17a 100644
--- a/editor/scene_tree_editor.h
+++ b/editor/scene_tree_editor.h
@@ -114,8 +114,6 @@ class SceneTreeEditor : public Control {
void _node_visibility_changed(Node *p_node);
void _update_visibility_color(Node *p_node, TreeItem *p_item);
- void _node_replace_owner(Node *p_base, Node *p_node, Node *p_root);
-
void _selection_changed();
Node *get_scene_node();
@@ -173,7 +171,6 @@ class SceneTreeDialog : public ConfirmationDialog {
//Button *cancel;
LineEdit *filter;
- void update_tree();
void _select();
void _cancel();
void _filter_changed(const String &p_filter);
diff --git a/editor/translations/ar.po b/editor/translations/ar.po
index 1ba7d4f786..67b76771d2 100644
--- a/editor/translations/ar.po
+++ b/editor/translations/ar.po
@@ -1008,7 +1008,7 @@ msgstr "لا نتائج من أجل \"%s\"."
#: editor/create_dialog.cpp editor/property_selector.cpp
msgid "No description available for %s."
-msgstr "ليس هناك وص٠مناسب لأجل s%."
+msgstr "ليس هناك وص٠مناسب لأجل %s."
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -4262,15 +4262,15 @@ msgstr "جاري البحث..."
#: editor/find_in_files.cpp
msgid "%d match in %d file."
-msgstr "d% تطابق ÙÙŠ d% الملÙ."
+msgstr "%d تطابق ÙÙŠ %d الملÙ."
#: editor/find_in_files.cpp
msgid "%d matches in %d file."
-msgstr "d% تطابقات ÙÙŠ d% الملÙ."
+msgstr "%d تطابقات ÙÙŠ %d الملÙ."
#: editor/find_in_files.cpp
msgid "%d matches in %d files."
-msgstr "d% تطابقات ÙÙŠ d% الملÙ."
+msgstr "%d تطابقات ÙÙŠ %d الملÙ."
#: editor/groups_editor.cpp
msgid "Add to Group"
diff --git a/editor/translations/ca.po b/editor/translations/ca.po
index e6fd202dde..26f7646086 100644
--- a/editor/translations/ca.po
+++ b/editor/translations/ca.po
@@ -21,7 +21,7 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-10-13 09:34+0000\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
"Last-Translator: Xavier Gomez <hiulit@gmail.com>\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/godot-engine/"
"godot/ca/>\n"
@@ -747,7 +747,6 @@ msgid "Whole Words"
msgstr "Paraules senceres"
#: editor/code_editor.cpp
-#, fuzzy
msgid "Replace"
msgstr "Reemplaçar"
@@ -2158,7 +2157,6 @@ msgid "Constants"
msgstr "Constants"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Property Descriptions"
msgstr "Descripcions de la Propietat"
@@ -2662,7 +2660,6 @@ msgid "Reload Saved Scene"
msgstr "Desa Escena"
#: editor/editor_node.cpp
-#, fuzzy
msgid ""
"The current scene has unsaved changes.\n"
"Reload the saved scene anyway? This action cannot be undone."
@@ -3194,9 +3191,8 @@ msgid "Suggest a Feature"
msgstr ""
#: editor/editor_node.cpp
-#, fuzzy
msgid "Send Docs Feedback"
-msgstr "Enviar suggeriments sobre la documentació"
+msgstr "Enviar suggeriments sobre la Documentació"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
msgid "Community"
@@ -3220,9 +3216,8 @@ msgid "Play"
msgstr "Reprodueix"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Pause the scene execution for debugging."
-msgstr "Pausa l’execució d’escena per a la depuració."
+msgstr "Posa en pausa l'execució de l'escena per a depurar-la."
#: editor/editor_node.cpp
msgid "Pause Scene"
@@ -3782,12 +3777,11 @@ msgid "Cannot remove temporary file:"
msgstr "No es pot desar el Tema:"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid ""
"Templates installation failed.\n"
"The problematic templates archives can be found at '%s'."
msgstr ""
-"No s'han pogut instal·lar les plantilles. \n"
+"No s'han pogut instal·lar les plantilles.\n"
"Les plantilles problemàtics es troben a '%s'."
#: editor/export_template_manager.cpp
@@ -4906,11 +4900,10 @@ msgstr ""
"els noms de les pistes."
#: editor/plugins/animation_blend_tree_editor_plugin.cpp
-#, fuzzy
msgid "Player path set is invalid, so unable to retrieve track names."
msgstr ""
-"El camí del reproductor assignat no és vàlid, de manera que no pot recuperar "
-"els noms de les pistes."
+"El camí del reproductor assignat no és vàlid, de manera que no s'ha pogut "
+"recuperar els noms de les pistes."
#: editor/plugins/animation_blend_tree_editor_plugin.cpp
#: editor/plugins/root_motion_editor_plugin.cpp
@@ -5136,7 +5129,6 @@ msgid "Include Gizmos (3D)"
msgstr "Inclou Gizmos (3D)"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Pin AnimationPlayer"
msgstr "Fixar AnimationPlayer"
@@ -5468,14 +5460,12 @@ msgid "Redirect loop."
msgstr "Bucle de redirecció."
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Request failed, timeout"
msgstr "La sol·licitud ha fallat, s'ha esgotat el temps d'espera"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Timeout."
-msgstr "Temps esgotat."
+msgstr "Temps d'espera esgotat."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Failed:"
@@ -5539,7 +5529,6 @@ msgid "Download for this asset is already in progress!"
msgstr "Ja s'està baixant aquest actiu!"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Recently Updated"
msgstr "Actualitzat Recentment"
@@ -5548,12 +5537,10 @@ msgid "Least Recently Updated"
msgstr "Actualitzacions menys recents"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Name (A-Z)"
msgstr "Nom (A-Z)"
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Name (Z-A)"
msgstr "Nom (Z-A)"
@@ -5707,9 +5694,8 @@ msgid "Grid Step:"
msgstr "Pas de la Graella:"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Primary Line Every:"
-msgstr "Línia principal cada:"
+msgstr "Línia Principal Cada:"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
@@ -6100,7 +6086,6 @@ msgid "Snapping Options"
msgstr "Opcions d'Ajustament"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Use Rotation Snap"
msgstr "Utilitzar Ajustament de Rotació"
@@ -6131,7 +6116,6 @@ msgid "Snap to Parent"
msgstr "Ajustar al Pare"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to Node Anchor"
msgstr "Ajustar a l'Àncora del Node"
@@ -6144,9 +6128,8 @@ msgid "Snap to Node Center"
msgstr "Ajustar al centre del node"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to Other Nodes"
-msgstr "Ajustar als altres nodes"
+msgstr "Ajustar a altres Nodes"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to Guides"
@@ -6181,9 +6164,8 @@ msgid "Show Bones"
msgstr "Mostra els Ossos"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Make Custom Bone(s) from Node(s)"
-msgstr "Fer os(sos) personalitzat(s) a partir de Node(s)"
+msgstr "Crear os(sos) personalitzat(s) a partir de Node(s)"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
@@ -6233,9 +6215,8 @@ msgid "Frame Selection"
msgstr "Enquadra la Selecció"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Preview Canvas Scale"
-msgstr "Vista prèvia de l'escala del llenç"
+msgstr "Vista prèvia de l'Escala del Llenç"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Translation mask for inserting keys."
@@ -6578,9 +6559,8 @@ msgid "Mesh is empty!"
msgstr "La malla és buida!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Couldn't create a Trimesh collision shape."
-msgstr "Crea una Col·lisió entre malles de triangles germanes."
+msgstr "No s'ha pogut crear una forma de col·lisió Trimesh."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Static Trimesh Body"
@@ -6772,18 +6752,16 @@ msgid "Remove item %d?"
msgstr "Elimina l'element %d?"
#: editor/plugins/mesh_library_editor_plugin.cpp
-#, fuzzy
msgid ""
"Update from existing scene?:\n"
"%s"
msgstr ""
-"Actualitzar des d'una 'Escena existent?:\n"
+"Actualitzar des de l'Escena existent?:\n"
"%s"
#: editor/plugins/mesh_library_editor_plugin.cpp
-#, fuzzy
msgid "Mesh Library"
-msgstr "Biblioteca de Models (MeshLibrary)"
+msgstr "Biblioteca de Malles"
#: editor/plugins/mesh_library_editor_plugin.cpp
msgid "Add Item"
@@ -6906,7 +6884,6 @@ msgstr "Crea un Polígon de Navegació"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "Convert to CPUParticles"
msgstr "Convertir a ParticulesCPU"
@@ -6942,9 +6919,8 @@ msgid "The geometry doesn't contain any faces."
msgstr "El Node no conté cap geometria (cares)."
#: editor/plugins/particles_editor_plugin.cpp
-#, fuzzy
msgid "\"%s\" doesn't inherit from Spatial."
-msgstr "\"% s\" no hereta de Spatial."
+msgstr "\"%s\" no hereta de Spatial."
#: editor/plugins/particles_editor_plugin.cpp
#, fuzzy
@@ -7253,14 +7229,12 @@ msgid "Scale Polygon"
msgstr "Escala el Polígon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
-#, fuzzy
msgid "Create a custom polygon. Enables custom polygon rendering."
msgstr ""
-"Crear polígon personalitzat. Habilita el renderitzat de polígons "
+"Crear un polígon personalitzat. S'habilita el renderitzat de polígons "
"personalitzats."
#: editor/plugins/polygon_2d_editor_plugin.cpp
-#, fuzzy
msgid ""
"Remove a custom polygon. If none remain, custom polygon rendering is "
"disabled."
@@ -7273,9 +7247,8 @@ msgid "Paint weights with specified intensity."
msgstr "Pinta pesos amb la intensitat especificada."
#: editor/plugins/polygon_2d_editor_plugin.cpp
-#, fuzzy
msgid "Unpaint weights with specified intensity."
-msgstr "Despinta el pes amb la intensitat especificada."
+msgstr "Despinta pesos amb la intensitat especificada."
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Radius:"
@@ -7424,7 +7397,6 @@ msgid "AnimationTree has no path set to an AnimationPlayer"
msgstr "L'AnimationTree no té ruta assignada cap a un AnimationPlayer"
#: editor/plugins/root_motion_editor_plugin.cpp
-#, fuzzy
msgid "Path to AnimationPlayer is invalid"
msgstr "El camí cap a l'AnimationPlayer no és vàlid"
@@ -7751,7 +7723,6 @@ msgstr ""
"No s'hi poden afegir els nodes ja que l'escena no utilitza l'script '%s' ."
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Lookup Symbol"
msgstr "Cercar Símbol"
@@ -7864,7 +7835,6 @@ msgid "Contextual Help"
msgstr "Ajuda Contextual"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Toggle Bookmark"
msgstr "Commutar Marcador"
@@ -7873,9 +7843,8 @@ msgid "Go to Next Bookmark"
msgstr "Anar al marcador següent"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Go to Previous Bookmark"
-msgstr "Anar al marcador anterior"
+msgstr "Anar al Marcador Anterior"
#: editor/plugins/script_text_editor.cpp
msgid "Remove All Bookmarks"
@@ -7919,9 +7888,8 @@ msgid "Shader"
msgstr "Shader"
#: editor/plugins/skeleton_2d_editor_plugin.cpp
-#, fuzzy
msgid "This skeleton has no bones, create some children Bone2D nodes."
-msgstr "Aquest esquelet no té ossos, crea alguns nodes fill Bone2D."
+msgstr "Aquest esquelet no té ossos, crea alguns nodes Bone2D fills."
#: editor/plugins/skeleton_2d_editor_plugin.cpp
#, fuzzy
@@ -7929,9 +7897,8 @@ msgid "Create Rest Pose from Bones"
msgstr "Crea Punts d'Emissió des d'una Malla"
#: editor/plugins/skeleton_2d_editor_plugin.cpp
-#, fuzzy
msgid "Set Rest Pose to Bones"
-msgstr "Estableix la postura de repòs als ossos"
+msgstr "Estableix la Postura de Repòs als Ossos"
#: editor/plugins/skeleton_2d_editor_plugin.cpp
msgid "Skeleton2D"
@@ -7960,7 +7927,6 @@ msgid "Create physical skeleton"
msgstr "Crea esquelet físic"
#: editor/plugins/skeleton_ik_editor_plugin.cpp
-#, fuzzy
msgid "Play IK"
msgstr "Reproduir IK"
@@ -8252,7 +8218,6 @@ msgid "Cinematic Preview"
msgstr "Previsualització Cinemàtica"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Not available when using the GLES2 renderer."
msgstr "No disponible quan s'utilitza el renderitzador GLES2."
@@ -8304,13 +8269,13 @@ msgid ""
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid ""
"Note: The FPS value displayed is the editor's framerate.\n"
"It cannot be used as a reliable indication of in-game performance."
msgstr ""
-"Nota: el valor FPS mostrat és la taxa de fotogrames de l'editor.\n"
-"No es pot utilitzar com una indicació fiable del rendiment en el joc."
+"Nota: El valor FPS que es mostra és el percentatge de fotogrames de "
+"l'editor.\n"
+"No es pot utilitzar com a indicació fiable del rendiment del joc."
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
@@ -8336,9 +8301,8 @@ msgid "Snap Nodes to Floor"
msgstr "Ajustar Nodes al Terra"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Couldn't find a solid floor to snap the selection to."
-msgstr "No ha pogut trobar un terra sòlid per ajustar la selecció."
+msgstr "No s'ha pogut trobar un sòl sòlid on ajustar la selecció."
#: editor/plugins/spatial_editor_plugin.cpp
#, fuzzy
@@ -8487,17 +8451,14 @@ msgid "Snap Settings"
msgstr "Configuració d'Ajustament"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Translate Snap:"
msgstr "Ajustament de Translació:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Rotate Snap (deg.):"
msgstr "Ajustament de Rotació (graus):"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Scale Snap (%):"
msgstr "Ajustament d'Escala (%):"
@@ -8588,9 +8549,8 @@ msgid "LightOccluder2D Preview"
msgstr "Crea un Polígon Oclusor"
#: editor/plugins/sprite_editor_plugin.cpp
-#, fuzzy
msgid "Sprite is empty!"
-msgstr "El Sprite està buit!"
+msgstr "L'Sprite està buit!"
#: editor/plugins/sprite_editor_plugin.cpp
msgid "Can't convert a sprite using animation frames to mesh."
@@ -8623,9 +8583,8 @@ msgid "Create CollisionPolygon2D Sibling"
msgstr "Crea un Polígon de Navegació"
#: editor/plugins/sprite_editor_plugin.cpp
-#, fuzzy
msgid "Invalid geometry, can't create light occluder."
-msgstr "La geometria no és vàlida, no es pot crear oclusor de llum."
+msgstr "La geometria no és vàlida; no es pot crear un oclusor de llum."
#: editor/plugins/sprite_editor_plugin.cpp
#, fuzzy
@@ -8657,12 +8616,10 @@ msgid "Settings:"
msgstr "Configuració:"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "No Frames Selected"
-msgstr "No hi ha Fotogrames Seleccionats"
+msgstr "No s'ha seleccionat cap fotograma"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Add %d Frame(s)"
msgstr "Afegir %d Fotograma(es)"
@@ -8729,9 +8686,8 @@ msgid "Add a Texture from File"
msgstr "Afegir Textura des de Fitxer"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Add Frames from a Sprite Sheet"
-msgstr "Afegir fotogrames des d'una fulla de Sprites"
+msgstr "Afegir fotogrames des d'un full d'Sprites"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Insert Empty (Before)"
@@ -8750,7 +8706,6 @@ msgid "Move (After)"
msgstr "Mou (Després)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "Select Frames"
msgstr "Seleccionar Fotogrames"
@@ -8789,9 +8744,8 @@ msgid "Snap Mode:"
msgstr "Mode d'ajustament:"
#: editor/plugins/texture_region_editor_plugin.cpp
-#, fuzzy
msgid "Pixel Snap"
-msgstr "Ajustar amb els Píxels"
+msgstr "Ajustament de Píxels"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Grid Snap"
@@ -9310,9 +9264,8 @@ msgid ""
msgstr ""
#: editor/plugins/theme_editor_preview.cpp
-#, fuzzy
msgid "Toggle Button"
-msgstr "Botó de commutació"
+msgstr "Botó de Commutació"
#: editor/plugins/theme_editor_preview.cpp
msgid "Disabled Button"
@@ -9391,7 +9344,6 @@ msgid "Editable Item"
msgstr "Element Editable"
#: editor/plugins/theme_editor_preview.cpp
-#, fuzzy
msgid "Subtree"
msgstr "Subarbre"
@@ -9421,9 +9373,8 @@ msgid "Erase Selection"
msgstr "Elimina la Selecció"
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Fix Invalid Tiles"
-msgstr "Arreglar Rajoles no Valides"
+msgstr "Arreglar Tiles no vàlides"
#: editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
@@ -9468,9 +9419,8 @@ msgid "Enable Priority"
msgstr "Habilitar Prioritat"
#: editor/plugins/tile_map_editor_plugin.cpp
-#, fuzzy
msgid "Filter tiles"
-msgstr "Filtrat de Fitxers"
+msgstr "Filtrar tiles"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Give a TileSet resource to this TileMap to use its tiles."
@@ -9523,14 +9473,12 @@ msgid "Clear Transform"
msgstr "Restablir Transformació"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Add Texture(s) to TileSet."
-msgstr "Afegeix Nodes des d'Arbre."
+msgstr "Afegir Textura(es) al TileSet"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Remove selected Texture from TileSet."
-msgstr "Elimineu la textura seleccionada de TileSet."
+msgstr "Eliminar la textura seleccionada del TileSet."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Create from Scene"
@@ -9559,18 +9507,16 @@ msgid "Next Coordinate"
msgstr "Coordenada Següent"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select the next shape, subtile, or Tile."
-msgstr "Seleccioneu la forma, sub-rajola o rajola següent."
+msgstr "Seleccioneu la següent forma, subtile o Tile."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Previous Coordinate"
msgstr "Coordenada Anterior"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select the previous shape, subtile, or Tile."
-msgstr "Seleccioneu la forma, sub-rajola o rajola anterior."
+msgstr "Seleccioneu la forma, el subtile o el Tile anterior."
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
@@ -9648,12 +9594,10 @@ msgid "Copy bitmask."
msgstr "Copiar màscara de bits."
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Paste bitmask."
msgstr "Enganxar màscara de bits."
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Erase bitmask."
msgstr "Esborrar màscara de bits."
@@ -9681,9 +9625,8 @@ msgid "Delete Selected Shape"
msgstr "Elimina Seleccionats"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Keep polygon inside region Rect."
-msgstr "Mantenir polígon dins de la regió Rect."
+msgstr "Mantenir el polígon dins de la regió Rect."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Enable snap and show grid (configurable via the Inspector)."
@@ -9737,9 +9680,8 @@ msgstr ""
"Clica en una altra Peça per a editar-lo."
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Delete selected Rect."
-msgstr "Suprimir Rectangle seleccionat."
+msgstr "Suprimir el Rect seleccionat."
#: editor/plugins/tile_set_editor_plugin.cpp
#, fuzzy
@@ -10385,7 +10327,6 @@ msgid "Returns the arc-sine of the parameter."
msgstr "Retorna l'arc sinus del paràmetre."
#: editor/plugins/visual_shader_editor_plugin.cpp
-#, fuzzy
msgid "Returns the inverse hyperbolic sine of the parameter."
msgstr "Retorna el sinus hiperbòlic invers del paràmetre."
diff --git a/editor/translations/de.po b/editor/translations/de.po
index 572c2afb2f..9f6f7ee0bf 100644
--- a/editor/translations/de.po
+++ b/editor/translations/de.po
@@ -72,13 +72,14 @@
# Philipp Wabnitz <philipp.wabnitz@s2011.tu-chemnitz.de>, 2021.
# jmih03 <joerni@mail.de>, 2021.
# Dominik Moos <dominik.moos@protonmail.com>, 2021.
+# Zae Chao <zaevi@live.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-09-22 20:30+0000\n"
-"Last-Translator: So Wieso <sowieso@dukun.de>\n"
+"PO-Revision-Date: 2021-10-23 10:13+0000\n"
+"Last-Translator: Zae Chao <zaevi@live.com>\n"
"Language-Team: German <https://hosted.weblate.org/projects/godot-engine/"
"godot/de/>\n"
"Language: de\n"
@@ -2487,9 +2488,8 @@ msgstr ""
"(Instanzen oder Vererbungen) nicht erfüllt."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Konvertierte Textur konnte nicht gespeichert werden:"
+msgstr "Eine oder mehrere Szenen konnten nicht gespeichert werden!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2632,7 +2632,7 @@ msgstr "Änderungen in ‚%s‘ vor dem Schließen speichern?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "%s existiert nicht mehr! Bitte anderen Ort zum Speichern wählen."
#: editor/editor_node.cpp
msgid ""
@@ -4491,6 +4491,12 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Es existieren ausstehende Änderungen die noch nicht angewandt wurden. Um "
+"Änderungen der Importoptionen anzuwenden, bitte den Reimportieren-Knopf "
+"klicken.\n"
+"Eine andere Ressource in der Dateisystemleiste auszuwählen, ohne zuvor den "
+"Reimportieren-Knopf zu betätigen, verwirft sämtliche ausstehende Änderungen "
+"der Importoptionen."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -5775,7 +5781,7 @@ msgstr "%d CanvasItems verschieben"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move CanvasItem \"%s\" to (%d, %d)"
-msgstr "CanvasItem „%s“ zu (%d, d%) verschieben"
+msgstr "CanvasItem „%s“ zu (%d, %d) verschieben"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -7476,12 +7482,10 @@ msgid "Move Down"
msgstr "Schiebe runter"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Nächstes Skript"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Vorheriges Skript"
@@ -7901,9 +7905,8 @@ msgid "Left Orthogonal"
msgstr "Links orthogonal"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspektivisch"
+msgstr "Linke Perspektive"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8265,24 +8268,23 @@ msgstr "Sicht von rechts"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Orbitsicht unten"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Orbitsicht links"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Orbitsicht rechts"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Sicht von vorne"
+msgstr "Orbitsicht oben"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Orbitsicht 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -14651,6 +14653,10 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Sehr kurze Schaltzeiten (< 0,05 Sekunden) können instabile Ergebnisse "
+"abhängig von der Bildfrequenz liefern.\n"
+"Für sehr kurze Schaltzeiten wird empfohlen, statt eines Timers, die "
+"›process‹-Schleife eines Skripts zu benutzen."
#: scene/main/viewport.cpp
msgid ""
@@ -14666,12 +14672,12 @@ msgstr ""
"irgendeinem Node zum Anzeigen zugewiesen werden."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
msgstr ""
-"Die Größe des Viewports muss größer als 0 sein um etwas rendern zu können."
+"Die Größe des Viewports muss mindestes 2 Pixel in beiden Dimensionen "
+"betragen um überhaupt irgendetwas rendern zu können."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/editor/translations/es.po b/editor/translations/es.po
index 522575a8fc..bc314e8294 100644
--- a/editor/translations/es.po
+++ b/editor/translations/es.po
@@ -75,8 +75,8 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-10-02 10:08+0000\n"
-"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n"
+"PO-Revision-Date: 2021-10-23 10:13+0000\n"
+"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/"
"godot/es/>\n"
"Language: es\n"
@@ -2483,9 +2483,8 @@ msgstr ""
"herencias) no se pudieron resolver."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "No se pudo guardar la textura convertida:"
+msgstr "¡No se ha podido guardar una o varias escenas!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2630,6 +2629,7 @@ msgstr "¿Guardar cambios de '%s' antes de cerrar?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
msgstr ""
+"¡%s ya no existe! Por favor, especifica una nueva ubicación de guardado."
#: editor/editor_node.cpp
msgid ""
@@ -4491,6 +4491,11 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Tienes cambios pendientes que aún no se han aplicado. Haz clic en Reimportar "
+"para aplicar los cambios realizados en las opciones de importación.\n"
+"Si seleccionas otro recurso en el dock Sistema de Archivos sin hacer clic en "
+"Reimportar primero, se descartarán los cambios realizados en el dock "
+"Importar."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7479,14 +7484,12 @@ msgid "Move Down"
msgstr "Bajar"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
-msgstr "Script siguiente"
+msgstr "Siguiente Script"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
-msgstr "Script anterior"
+msgstr "Script Anterior"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
@@ -7902,9 +7905,8 @@ msgid "Left Orthogonal"
msgstr "Ortogonal Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspectiva"
+msgstr "Perspectiva Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8263,24 +8265,23 @@ msgstr "Vista Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Vista de Órbita Inferior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Vista de Órbita Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Vista de Órbita Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Vista Frontal"
+msgstr "Vista de Órbita Superior"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Vista de Órbita 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -13528,11 +13529,11 @@ msgstr ""
#: platform/android/export/export_plugin.cpp
msgid "Signing debug %s..."
-msgstr "Firma de depuración %s..."
+msgstr "Firmando %s debug..."
#: platform/android/export/export_plugin.cpp
msgid "Signing release %s..."
-msgstr "Firmando liberación %s..."
+msgstr "Firmando %s release..."
#: platform/android/export/export_plugin.cpp
msgid "Could not find keystore, unable to export."
@@ -14640,6 +14641,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Los tiempos de espera del temporizador muy bajos (< 0,05 segundos) pueden "
+"comportarse de manera significativamente diferente dependiendo de la "
+"velocidad de fotogramas renderizados o de la física.\n"
+"Considera la posibilidad de utilizar un bucle en process dentro del script "
+"en lugar de depender de un Timer para tiempos de espera muy bajos."
#: scene/main/viewport.cpp
msgid ""
@@ -14655,13 +14661,12 @@ msgstr ""
"nodo para que se muestre."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
msgstr ""
-"El tamaño del Viewport debe ser mayor que 0 para poder renderizar cualquier "
-"cosa."
+"El tamaño del Viewport debe ser mayor o igual a 2 píxeles en ambas "
+"dimensiones para renderizar cualquier cosa."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po
index 76afb25668..dbff18af4e 100644
--- a/editor/translations/es_AR.po
+++ b/editor/translations/es_AR.po
@@ -24,7 +24,7 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-10-10 10:18+0000\n"
+"PO-Revision-Date: 2021-10-23 10:13+0000\n"
"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n"
"Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/"
"godot-engine/godot/es_AR/>\n"
@@ -2427,9 +2427,8 @@ msgstr ""
"dependencias (instancias o herencia)."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "No se pudo guardar la textura convertida:"
+msgstr "¡No se ha podido guardar una o varias escenas!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2575,6 +2574,7 @@ msgstr "Guardar cambios a '%s' antes de cerrar?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
msgstr ""
+"¡%s ya no existe! Por favor, especificá una nueva ubicación de guardado."
#: editor/editor_node.cpp
msgid ""
@@ -3881,7 +3881,7 @@ msgstr "Abrir Carpeta"
#: editor/export_template_manager.cpp
msgid "Open the folder containing installed templates for the current version."
msgstr ""
-"Abra la carpeta que contiene las plantillas instaladas para la versión "
+"Abrir la carpeta que contiene las plantillas instaladas para la versión "
"actual."
#: editor/export_template_manager.cpp
@@ -4433,6 +4433,11 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Tenés cambios pendientes que aún no se han aplicado. Hacé clic en Reimportar "
+"para aplicar los cambios realizados en las opciones de importación.\n"
+"Si seleccionás otro recurso en el dock Sistema de Archivos sin hacer clic en "
+"Reimportar primero, se descartarán los cambios realizados en el dock "
+"Importar."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -5924,7 +5929,7 @@ msgstr "Arrastrar: Rotar el nodo seleccionado alrededor del pivote."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+Drag: Move selected node."
-msgstr "Alt+Arrastrar: Mover el nodo seleccionado"
+msgstr "Alt+Arrastrar: Mover el nodo seleccionado."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "V: Set selected node's pivot position."
@@ -7268,16 +7273,15 @@ msgstr "Generar Puntos en la Room"
#: editor/plugins/room_manager_editor_plugin.cpp
msgid "Generate Points"
-msgstr "Conteo de Puntos Generados:"
+msgstr "Generar puntos"
#: editor/plugins/room_manager_editor_plugin.cpp
msgid "Flip Portal"
msgstr "Invertir Portal"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Occluder Set Transform"
-msgstr "Reestablecer Transform"
+msgstr "Transform de Occluder Set"
#: editor/plugins/room_manager_editor_plugin.cpp
msgid "Center Node"
@@ -7417,14 +7421,12 @@ msgid "Move Down"
msgstr "Bajar"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
-msgstr "Script siguiente"
+msgstr "Script Siguiente"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
-msgstr "Script anterior"
+msgstr "Script Anterior"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
@@ -7840,9 +7842,8 @@ msgid "Left Orthogonal"
msgstr "Ortogonal Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspectiva"
+msgstr "Perspectiva Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8201,24 +8202,23 @@ msgstr "Vista Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Orbitar Vista Hacia Abajo"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Orbitar Vista Hacia La Izquierda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Orbitar Vista Hacia La Derecha"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Vista Frontal"
+msgstr "Orbitar Vista Hacia Arriba"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Orbitar Vista 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -8716,7 +8716,7 @@ msgstr "Seleccione todos los elementos visibles de color y sus datos."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Deselect all visible color items."
-msgstr "Quitar selección a todos los elementos visibles de color."
+msgstr "Quitar selección a todos los items de color visibles."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all visible constant items."
@@ -8724,11 +8724,11 @@ msgstr "Seleccionar todos elementos constant visibles."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all visible constant items and their data."
-msgstr "Seleccionar todos los elementos constant visibles y sus datos."
+msgstr "Seleccionar todos los items visibles constantes y sus datos."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Deselect all visible constant items."
-msgstr "Deseleccionar todos los elementos constant visibles."
+msgstr "Quitar selección a todos los items visibles constantes."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all visible font items."
@@ -8736,7 +8736,7 @@ msgstr "Seleccionar todos los elementos font visibles."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all visible font items and their data."
-msgstr "Seleccionar todos los elementos font visibles y sus datos."
+msgstr "Seleccionar todos los items visibles y sus datos."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Deselect all visible font items."
@@ -11820,6 +11820,11 @@ msgid ""
"FileSystem dock context menu\n"
"or create an inherited scene using Scene > New Inherited Scene... instead."
msgstr ""
+"No se puede guardar la rama del nodo raíz como una escena instanciada.\n"
+"Para crear una copia editable de la escena actual, duplicala usando el menú "
+"contextual del dock Sistema de Archivos\n"
+"o crea una escena heredada usando Escena > Nueva Escena Heredada... en su "
+"lugar."
#: editor/scene_tree_dock.cpp
msgid ""
@@ -11827,6 +11832,10 @@ msgid ""
"To create a variation of a scene, you can make an inherited scene based on "
"the instanced scene using Scene > New Inherited Scene... instead."
msgstr ""
+"No se puede guardar la rama de una escena ya instanciada.\n"
+"Para crear una variación de una escena, puedes hacer una escena heredada "
+"basada en la escena instanciada usando Escena > Nueva Escena Heredada... en "
+"su lugar."
#: editor/scene_tree_dock.cpp
msgid "Save New Scene As..."
@@ -12488,24 +12497,20 @@ msgid "Change Ray Shape Length"
msgstr "Cambiar Largo de Shape Rayo"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Room Point Position"
-msgstr "Setear Posición de Punto de Curva"
+msgstr "Establecer Posición del Room Point"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Portal Point Position"
-msgstr "Setear Posición de Punto de Curva"
+msgstr "Establecer Posición del Portal Point"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Radius"
-msgstr "Cambiar Radio de Shape Cilindro"
+msgstr "Establecer Radio de la Esfera de Oclusión"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Position"
-msgstr "Setear Posición de Entrada de Curva"
+msgstr "Establecer Posición de la Esfera de Oclusión"
#: modules/csg/csg_gizmos.cpp
msgid "Change Cylinder Radius"
@@ -12619,14 +12624,12 @@ msgid "Object can't provide a length."
msgstr "El objeto no puede proveer un largo."
#: modules/gltf/editor_scene_exporter_gltf_plugin.cpp
-#, fuzzy
msgid "Export Mesh GLTF2"
-msgstr "Exportar Librería de Meshes"
+msgstr "Exportar Malla GLTF2"
#: modules/gltf/editor_scene_exporter_gltf_plugin.cpp
-#, fuzzy
msgid "Export GLTF..."
-msgstr "Exportar..."
+msgstr "Exportar GLTF..."
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Plane"
@@ -12669,9 +12672,8 @@ msgid "GridMap Paint"
msgstr "Pintar GridMap"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "GridMap Selection"
-msgstr "Llenar Selección en GridMap"
+msgstr "Selección de GridMap"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Grid Map"
@@ -12794,9 +12796,8 @@ msgid "Class name can't be a reserved keyword"
msgstr "El nombre de la clase no puede ser una palabra reservada"
#: modules/mono/csharp_script.cpp
-#, fuzzy
msgid "Build Solution"
-msgstr "Llenar la Selección"
+msgstr "Construir Solución"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
@@ -12928,14 +12929,12 @@ msgid "Add Output Port"
msgstr "Agregar Puerto de Salida"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Port Type"
-msgstr "Cambiar Tipo"
+msgstr "Cambiar Tipo de Puerto"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Change Port Name"
-msgstr "Cambiar nombre del puerto de entrada"
+msgstr "Cambiar Nombre de Puerto"
#: modules/visual_script/visual_script_editor.cpp
msgid "Override an existing built-in function."
@@ -13050,9 +13049,8 @@ msgid "Add Preload Node"
msgstr "Agregar Nodo Preload"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Add Node(s)"
-msgstr "Agregar Nodo"
+msgstr "Agregar Nodo(s)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node(s) From Tree"
@@ -13319,37 +13317,31 @@ msgstr "Seleccionar dispositivo de la lista"
#: platform/android/export/export_plugin.cpp
msgid "Running on %s"
-msgstr ""
+msgstr "Ejecutando en %s"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Exporting APK..."
-msgstr "Exportar Todo"
+msgstr "Exportar APK..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Uninstalling..."
-msgstr "Desinstalar"
+msgstr "Desinstalando..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Installing to device, please wait..."
-msgstr "Cargando, esperá, por favor..."
+msgstr "Instalando en el dispositivo, espera por favor..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not install to device: %s"
-msgstr "No se pudo instanciar la escena!"
+msgstr "No se pudo instalar en el dispositivo: %s"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Running on device..."
-msgstr "Ejecutando Script Personalizado..."
+msgstr "Ejecutando en el dispositivo..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not execute on device."
-msgstr "No se pudo crear la carpeta."
+msgstr "No se ha podido ejecutar en el dispositivo."
#: platform/android/export/export_plugin.cpp
msgid "Unable to find the 'apksigner' tool."
@@ -13460,40 +13452,38 @@ msgid ""
"directory.\n"
"The resulting %s is unsigned."
msgstr ""
+"No se ha encontrado 'apksigner'.\n"
+"Por favor, comprobá que el comando esté disponible en el directorio Android "
+"SDK build-tools.\n"
+"El %s resultante está sin firmar."
#: platform/android/export/export_plugin.cpp
msgid "Signing debug %s..."
-msgstr ""
+msgstr "Firmando %s debug..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Signing release %s..."
-msgstr ""
-"Examinando Archivos,\n"
-"Aguardá, por favor."
+msgstr "Firmando %s release..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not find keystore, unable to export."
-msgstr "No se pudo abrir la plantilla para exportar:"
+msgstr "No se pudo encontrar la keystore, no se puedo exportar."
#: platform/android/export/export_plugin.cpp
msgid "'apksigner' returned with error #%d"
-msgstr ""
+msgstr "'apksigner' ha retornado con error #%d"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Verifying %s..."
-msgstr "Agregando %s..."
+msgstr "Verificando %s..."
#: platform/android/export/export_plugin.cpp
msgid "'apksigner' verification of %s failed."
-msgstr ""
+msgstr "La verificación de 'apksigner' de %s ha fallado."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Exporting for Android"
-msgstr "Exportar Todo"
+msgstr "Exportando para Android"
#: platform/android/export/export_plugin.cpp
msgid "Invalid filename! Android App Bundle requires the *.aab extension."
@@ -13510,7 +13500,7 @@ msgstr "¡Nombre de archivo inválido! Android APK requiere la extensión *.apk.
#: platform/android/export/export_plugin.cpp
msgid "Unsupported export format!\n"
-msgstr ""
+msgstr "¡Formato de exportación no soportado!\n"
#: platform/android/export/export_plugin.cpp
msgid ""
@@ -13538,16 +13528,17 @@ msgstr ""
msgid ""
"Unable to overwrite res://android/build/res/*.xml files with project name"
msgstr ""
+"No se pudieron sobrescribir los archivos res://android/build/res/*.xml con "
+"el nombre del proyecto"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not export project files to gradle project\n"
-msgstr "No se pudo obtener project.godot en la ruta de proyecto."
+msgstr ""
+"No se pudieron exportar los archivos del proyecto a un proyecto gradle\n"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not write expansion package file!"
-msgstr "No se pudo escribir el archivo:"
+msgstr "¡No se pudo escribir el archivo del paquete de expansión!"
#: platform/android/export/export_plugin.cpp
msgid "Building Android Project (gradle)"
@@ -13575,21 +13566,20 @@ msgstr ""
"directorio del proyecto de gradle para ver los resultados."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Package not found: %s"
-msgstr "No se encontró la animación: '%s'"
+msgstr "Paquete no encontrado:% s"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Creating APK..."
-msgstr "Creando contornos..."
+msgstr "Creando APK..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid ""
"Could not find template APK to export:\n"
"%s"
-msgstr "No se pudo abrir la plantilla para exportar:"
+msgstr ""
+"No se pudo encontrar la plantilla APK para exportar:\n"
+"%s"
#: platform/android/export/export_plugin.cpp
msgid ""
@@ -13598,24 +13588,26 @@ msgid ""
"Please build a template with all required libraries, or uncheck the missing "
"architectures in the export preset."
msgstr ""
+"Bibliotecas faltantes en la plantilla de exportación para las arquitecturas "
+"seleccionadas: %s.\n"
+"Por favor, construya una plantilla con todas las bibliotecas necesarias, o "
+"desmarque las arquitecturas faltantes en el preset de exportación."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Adding files..."
-msgstr "Agregando %s..."
+msgstr "Agregando archivos..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not export project files"
-msgstr "No se pudo escribir el archivo:"
+msgstr "No se pudieron exportar los archivos del proyecto"
#: platform/android/export/export_plugin.cpp
msgid "Aligning APK..."
-msgstr ""
+msgstr "Alineando APK..."
#: platform/android/export/export_plugin.cpp
msgid "Could not unzip temporary unaligned APK."
-msgstr ""
+msgstr "No se pudo descomprimir el APK temporal no alineado."
#: platform/iphone/export/export.cpp platform/osx/export/export.cpp
msgid "Identifier is missing."
@@ -13663,45 +13655,40 @@ msgid "Could not write file:"
msgstr "No se pudo escribir el archivo:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read file:"
-msgstr "No se pudo escribir el archivo:"
+msgstr "No se pudo leer el archivo:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read HTML shell:"
-msgstr "No se pudo leer el shell HTML personalizado:"
+msgstr "No se pudo leer el shell HTML:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not create HTTP server directory:"
-msgstr "No se pudo crear la carpeta."
+msgstr "No se pudo crear el directorio del servidor HTTP:"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Error starting HTTP server:"
-msgstr "Error al guardar escena."
+msgstr "Error al iniciar el servidor HTTP:"
#: platform/osx/export/export.cpp
-#, fuzzy
msgid "Invalid bundle identifier:"
-msgstr "Identificador inválido:"
+msgstr "Identificador de paquete no válido:"
#: platform/osx/export/export.cpp
msgid "Notarization: code signing required."
-msgstr ""
+msgstr "Notarización: se requiere firma de código."
#: platform/osx/export/export.cpp
msgid "Notarization: hardened runtime required."
-msgstr ""
+msgstr "Notarización: se requiere hardened runtime."
#: platform/osx/export/export.cpp
msgid "Notarization: Apple ID name not specified."
-msgstr ""
+msgstr "Notarización: nombre de ID de Apple no especificado."
#: platform/osx/export/export.cpp
msgid "Notarization: Apple ID password not specified."
-msgstr ""
+msgstr "Notarización: contraseña de ID de Apple no especificada."
#: platform/uwp/export/export.cpp
msgid "Invalid package short name."
@@ -14146,6 +14133,9 @@ msgid ""
"longer has any effect.\n"
"To remove this warning, disable the GIProbe's Compress property."
msgstr ""
+"La propiedad Compress de GIProbe ha quedado obsoleta debido a errores "
+"conocidos y ya no tiene ningún efecto.\n"
+"Para eliminar esta advertencia, desactiva la propiedad Compress de GIProbe."
#: scene/3d/light.cpp
msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows."
@@ -14168,11 +14158,11 @@ msgstr ""
#: scene/3d/occluder.cpp
msgid "No shape is set."
-msgstr ""
+msgstr "No se ha establecido ninguna forma."
#: scene/3d/occluder.cpp
msgid "Only uniform scales are supported."
-msgstr ""
+msgstr "Sólo se admiten escalas uniformes."
#: scene/3d/particles.cpp
msgid ""
@@ -14243,15 +14233,15 @@ msgstr "El nodo A y el nodo B deben ser diferentes PhysicsBody"
#: scene/3d/portal.cpp
msgid "The RoomManager should not be a child or grandchild of a Portal."
-msgstr ""
+msgstr "El RoomManager no debe ser hijo o nieto de un Portal."
#: scene/3d/portal.cpp
msgid "A Room should not be a child or grandchild of a Portal."
-msgstr ""
+msgstr "Una Room no debe ser hijo o nieto de un Portal."
#: scene/3d/portal.cpp
msgid "A RoomGroup should not be a child or grandchild of a Portal."
-msgstr ""
+msgstr "Un RoomGroup no debe ser hijo o nieto de un Portal."
#: scene/3d/remote_transform.cpp
msgid ""
@@ -14263,79 +14253,98 @@ msgstr ""
#: scene/3d/room.cpp
msgid "A Room cannot have another Room as a child or grandchild."
-msgstr ""
+msgstr "Una Room no puede tener otra Room como hija o nieta."
#: scene/3d/room.cpp
msgid "The RoomManager should not be placed inside a Room."
-msgstr ""
+msgstr "El RoomManager no debe ubicarse dentro de una Room."
#: scene/3d/room.cpp
msgid "A RoomGroup should not be placed inside a Room."
-msgstr ""
+msgstr "Un RoomGroup no debe colocarse dentro de una Room."
#: scene/3d/room.cpp
msgid ""
"Room convex hull contains a large number of planes.\n"
"Consider simplifying the room bound in order to increase performance."
msgstr ""
+"El cuerpo convexo de la room contiene un gran número de planos.\n"
+"Considera la posibilidad de simplificar los límites de la room para aumentar "
+"el rendimiento."
#: scene/3d/room_group.cpp
msgid "The RoomManager should not be placed inside a RoomGroup."
-msgstr ""
+msgstr "El RoomManager no debe colocarse dentro de un RoomGroup."
#: scene/3d/room_manager.cpp
msgid "The RoomList has not been assigned."
-msgstr ""
+msgstr "La RoomList no ha sido asignada."
#: scene/3d/room_manager.cpp
msgid "The RoomList node should be a Spatial (or derived from Spatial)."
-msgstr ""
+msgstr "El nodo RoomList debe ser un Spatial (o derivado de Spatial)."
#: scene/3d/room_manager.cpp
msgid ""
"Portal Depth Limit is set to Zero.\n"
"Only the Room that the Camera is in will render."
msgstr ""
+"El Límite de Profundidad del Portal está ajustado a cero.\n"
+"Sólo se renderizará la room en la que se encuentra la cámara."
#: scene/3d/room_manager.cpp
msgid "There should only be one RoomManager in the SceneTree."
-msgstr ""
+msgstr "Sólo debe haber un RoomManager en el SceneTree."
#: scene/3d/room_manager.cpp
msgid ""
"RoomList path is invalid.\n"
"Please check the RoomList branch has been assigned in the RoomManager."
msgstr ""
+"La ruta del RoomList no es válida.\n"
+"Por favor, comprueba que la rama de la RoomList ha sido asignada al "
+"RoomManager."
#: scene/3d/room_manager.cpp
msgid "RoomList contains no Rooms, aborting."
-msgstr ""
+msgstr "La RoomList no contiene Rooms, abortando."
#: scene/3d/room_manager.cpp
msgid "Misnamed nodes detected, check output log for details. Aborting."
msgstr ""
+"Nodos con nombres incorrectos detectados, comprueba la salida del Log para "
+"más detalles. Abortando."
#: scene/3d/room_manager.cpp
msgid "Portal link room not found, check output log for details."
msgstr ""
+"No se encuentra Portal link room, comprueba la salida del Log para más "
+"detalles."
#: scene/3d/room_manager.cpp
msgid ""
"Portal autolink failed, check output log for details.\n"
"Check the portal is facing outwards from the source room."
msgstr ""
+"Fallo en el Portal autolink, comprueba la salida del Log para más detalles.\n"
+"Comprueba si el portal está mirando hacia fuera de la room de origen."
#: scene/3d/room_manager.cpp
msgid ""
"Room overlap detected, cameras may work incorrectly in overlapping area.\n"
"Check output log for details."
msgstr ""
+"Detectada superposición de la Room, las cámaras pueden funcionar "
+"incorrectamente en las zonas donde hay superposición.\n"
+"Comrpueba la salida del Log para más detalles."
#: scene/3d/room_manager.cpp
msgid ""
"Error calculating room bounds.\n"
"Ensure all rooms contain geometry or manual bounds."
msgstr ""
+"Error al calcular los límites de la room.\n"
+"Asegurate de que todas las rooms contengan geometría o límites manuales."
#: scene/3d/soft_body.cpp
msgid "This body will be ignored until you set a mesh."
@@ -14401,7 +14410,7 @@ msgstr "No se encontró la animación: '%s'"
#: scene/animation/animation_player.cpp
msgid "Anim Apply Reset"
-msgstr ""
+msgstr "Aplicar Reset de Animación"
#: scene/animation/animation_tree.cpp
msgid "In node '%s', invalid animation: '%s'."
@@ -14510,6 +14519,10 @@ msgid ""
"The GLES2 backend is currently in use, so these modes will act like Stretch "
"instead."
msgstr ""
+"Las opciones Tile y Tile Fit para las propiedades Axis Stretch sólo son "
+"efectivas cuando se utiliza el backend de renderizado GLES3.\n"
+"El backend GLES2 está actualmente en uso, por lo que estos modos actuarán "
+"como Stretch en su lugar."
#: scene/gui/popup.cpp
msgid ""
@@ -14554,6 +14567,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Los tiempos de espera del temporizador muy bajos (< 0,05 segundos) pueden "
+"comportarse de manera significativamente diferente dependiendo de la "
+"velocidad de fotogramas renderizados o de la física.\n"
+"Considera la posibilidad de utilizar un bucle en process dentro del script "
+"en lugar de depender de un Timer para tiempos de espera muy bajos."
#: scene/main/viewport.cpp
msgid ""
@@ -14568,15 +14586,16 @@ msgstr ""
"textura interna a algún otro nodo para mostrar."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
-msgstr "El tamaño del viewport debe ser mayor a 0 para poder renderizar."
+msgstr ""
+"El tamaño del Viewport debe ser mayor o igual a 2 píxeles en ambas "
+"dimensiones para renderizar cualquier cosa."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
-msgstr ""
+msgstr "Establecer Esferas OccluderShapeSphere"
#: scene/resources/visual_shader_nodes.cpp
msgid ""
@@ -14599,25 +14618,29 @@ msgid "Invalid comparison function for that type."
msgstr "Función de comparación inválida para este tipo."
#: servers/visual/shader_language.cpp
-#, fuzzy
msgid "Varying may not be assigned in the '%s' function."
-msgstr "Solo se pueden asignar variaciones en funciones de vértice."
+msgstr "No se pueden asignar varyings a la función '%s'."
#: servers/visual/shader_language.cpp
msgid ""
"Varyings which assigned in 'vertex' function may not be reassigned in "
"'fragment' or 'light'."
msgstr ""
+"Las varyings que fueron asignadas en una función 'vertex' no pueden ser "
+"reasignadas en 'fragment' o 'light'."
#: servers/visual/shader_language.cpp
msgid ""
"Varyings which assigned in 'fragment' function may not be reassigned in "
"'vertex' or 'light'."
msgstr ""
+"Las varyings que fueron asignadas en una función 'fragment' no pueden ser "
+"reasignadas en 'vertex' o 'light'."
#: servers/visual/shader_language.cpp
msgid "Fragment-stage varying could not been accessed in custom function!"
msgstr ""
+"¡No se pudo acceder a la fragment-stage varying en la función personalizada!"
#: servers/visual/shader_language.cpp
msgid "Assignment to function."
diff --git a/editor/translations/fa.po b/editor/translations/fa.po
index c300e21510..e61aa0a6c7 100644
--- a/editor/translations/fa.po
+++ b/editor/translations/fa.po
@@ -363,7 +363,7 @@ msgstr "حذ٠ترک انیمشین"
#. TRANSLATORS: %s will be replaced by a phrase describing the target of track.
#: editor/animation_track_editor.cpp
msgid "Create NEW track for %s and insert key?"
-msgstr "یک ترک جدید برای s% بساز و کلید را درج کن؟"
+msgstr "یک ترک جدید برای %s بساز و کلید را درج کن؟"
#: editor/animation_track_editor.cpp
msgid "Create %d NEW tracks and insert keys?"
@@ -904,11 +904,11 @@ msgstr "سیگنال:"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr "'s%' را به 's%' متصل کن"
+msgstr "'%s' را به '%s' متصل کن"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
-msgstr "'s%' را از 's%' جدا کن"
+msgstr "'%s' را از '%s' جدا کن"
#: editor/connections_dialog.cpp
msgid "Disconnect all from signal: '%s'"
@@ -1024,7 +1024,7 @@ msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will only take effect when reloaded."
msgstr ""
-"ویرایش صحنه 's%' شروع شده است.\n"
+"ویرایش صحنه '%s' شروع شده است.\n"
"تغییرات تنها وقتی جلوه گرند که از نو بارگیری شوند."
#: editor/dependency_editor.cpp
@@ -1032,7 +1032,7 @@ msgid ""
"Resource '%s' is in use.\n"
"Changes will only take effect when reloaded."
msgstr ""
-"منابع 's%' بکار Ø±ÙØªÙ‡â€ŒØ§Ù†Ø¯.\n"
+"منابع '%s' بکار Ø±ÙØªÙ‡â€ŒØ§Ù†Ø¯.\n"
"تغییرات تنها وقتی جلوه گرند که از نو بارگیری شوند."
#: editor/dependency_editor.cpp
@@ -1134,7 +1134,7 @@ msgstr "خطا در بارگذاری!"
#: editor/dependency_editor.cpp
msgid "Permanently delete %d item(s)? (No undo!)"
-msgstr "به طور دائمی تعداد 'd%' آیتم را حذ٠کند؟ (بدون undo !)"
+msgstr "به طور دائمی تعداد '%d' آیتم را حذ٠کند؟ (بدون undo !)"
#: editor/dependency_editor.cpp
msgid "Show Dependencies"
@@ -1519,7 +1519,7 @@ msgstr "کلمه کلیدی نمی تواند به عنوان یک نام خود
#: editor/editor_autoload_settings.cpp
msgid "Autoload '%s' already exists!"
-msgstr "بارگذاری خودکار 's%' هم اکنون موجود است!"
+msgstr "بارگذاری خودکار '%s' هم اکنون موجود است!"
#: editor/editor_autoload_settings.cpp
msgid "Rename Autoload"
@@ -4191,7 +4191,7 @@ msgstr ""
#: editor/groups_editor.cpp
#, fuzzy
msgid "Group name already exists."
-msgstr "بارگذاری خودکار 's%' هم اکنون موجود است!"
+msgstr "بارگذاری خودکار '%s' هم اکنون موجود است!"
#: editor/groups_editor.cpp
#, fuzzy
@@ -4670,7 +4670,7 @@ msgstr "گره انیمیشن"
#: editor/plugins/animation_blend_space_2d_editor.cpp
#, fuzzy
msgid "Triangle already exists."
-msgstr "بارگذاری خودکار 's%' هم اکنون موجود است!"
+msgstr "بارگذاری خودکار '%s' هم اکنون موجود است!"
#: editor/plugins/animation_blend_space_2d_editor.cpp
#, fuzzy
@@ -4875,7 +4875,7 @@ msgstr "نام نامعتبر."
#: editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "Animation name already exists!"
-msgstr "بارگذاری خودکار 's%' هم اکنون موجود است!"
+msgstr "بارگذاری خودکار '%s' هم اکنون موجود است!"
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
@@ -7606,7 +7606,7 @@ msgstr ""
#, fuzzy
msgid ""
"Missing connected method '%s' for signal '%s' from node '%s' to node '%s'."
-msgstr "'s%' را از 's%' جدا کن"
+msgstr "'%s' را از '%s' جدا کن"
#: editor/plugins/script_text_editor.cpp
msgid "[Ignore]"
@@ -11277,7 +11277,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
#, fuzzy
msgid "An action with the name '%s' already exists."
-msgstr "بارگذاری خودکار 's%' هم اکنون موجود است!"
+msgstr "بارگذاری خودکار '%s' هم اکنون موجود است!"
#: editor/project_settings_editor.cpp
msgid "Rename Input Action Event"
@@ -13428,7 +13428,7 @@ msgstr "مسیر به یک نود نمیرسد!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name '%s' in node %s."
-msgstr "نام دارایی ایندکس نامعتبر 's%' در نود s%."
+msgstr "نام دارایی ایندکس نامعتبر '%s' در نود %s."
#: modules/visual_script/visual_script_nodes.cpp
msgid ": Invalid argument of type: "
@@ -14497,12 +14497,12 @@ msgstr ""
#: scene/animation/animation_tree.cpp
msgid "Invalid animation: '%s'."
-msgstr "انیمیشن نامعتبر: 's%'."
+msgstr "انیمیشن نامعتبر: '%s'."
#: scene/animation/animation_tree.cpp
#, fuzzy
msgid "Nothing connected to input '%s' of node '%s'."
-msgstr "'s%' را از 's%' جدا کن"
+msgstr "'%s' را از '%s' جدا کن"
#: scene/animation/animation_tree.cpp
msgid "No root AnimationNode for the graph is set."
@@ -14826,7 +14826,7 @@ msgstr "ثوابت قابل تغییر نیستند."
#~ msgstr "صحنه جدید"
#~ msgid "Replaced %d occurrence(s)."
-#~ msgstr "تعداد d% رخداد جایگزین شد."
+#~ msgstr "تعداد %d رخداد جایگزین شد."
#, fuzzy
#~ msgid "Brief Description"
@@ -15232,7 +15232,7 @@ msgstr "ثوابت قابل تغییر نیستند."
#~ "اسپرایت تنظیم شود تا کار کند."
#~ msgid "Method List For '%s':"
-#~ msgstr "لیست متد برای 's%' :"
+#~ msgstr "لیست متد برای '%s' :"
#~ msgid "Return:"
#~ msgstr "بازگشت:"
@@ -15273,7 +15273,7 @@ msgstr "ثوابت قابل تغییر نیستند."
#~ "SpatialSamplePlayer آهنگ را پخش کند."
#~ msgid "Replaced %d Ocurrence(s)."
-#~ msgstr "تعداد d% رخداد جایگزین شد."
+#~ msgstr "تعداد %d رخداد جایگزین شد."
#, fuzzy
#~ msgid "Create Android keystore"
diff --git a/editor/translations/fi.po b/editor/translations/fi.po
index d24317f129..a7dca1dd0d 100644
--- a/editor/translations/fi.po
+++ b/editor/translations/fi.po
@@ -17,7 +17,7 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-09-22 20:30+0000\n"
+"PO-Revision-Date: 2021-10-18 15:35+0000\n"
"Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n"
"Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/"
"godot/fi/>\n"
@@ -2407,9 +2407,8 @@ msgstr ""
"perintää) ei voida toteuttaa."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Aliprosessia ei voitu käynnistää!"
+msgstr "Yhtä tai useampaa skeneä ei voitu tallentaa!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2552,6 +2551,7 @@ msgstr "Tallennetaanko muutokset tiedostoon '%s' ennen sulkemista?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
msgstr ""
+"%s ei ole enää olemassa! Ole hyvä ja määrittele uusi tallennussijainti."
#: editor/editor_node.cpp
msgid ""
@@ -4390,6 +4390,11 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Sinulla on avoinna olevia muutoksia, joita ei ole vielä otettu käyttöön. "
+"Napsauta Tuo uudelleen ottaaksesi tuontivalinnoissa tehdyt muutokset "
+"käyttöön.\n"
+"Toisen resurssin valitseminen Tiedostojärjestelmä-telakassa ilman, että Tuo "
+"uudelleen on tehty hylkää Tuonti-telakassa tehdyt muutokset."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7366,12 +7371,10 @@ msgid "Move Down"
msgstr "Siirrä alas"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Seuraava skripti"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Edellinen skripti"
@@ -7788,9 +7791,8 @@ msgid "Left Orthogonal"
msgstr "Vasen ortogonaalinen"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspektiivi"
+msgstr "Vasen perspektiivi"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8149,24 +8151,23 @@ msgstr "Oikea näkymä"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Kiertoratanäkymä alas"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Kiertoratanäkymä vasemmalle"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Kiertoratanäkymä oikealle"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Etunäkymä"
+msgstr "Kiertoratanäkymä ylös"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Kiertoratanäkymä 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -14475,6 +14476,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Hyvin alhaiset ajastimen odotusajat (< 0,05 sekuntia) saattavat käyttäytyä "
+"merkittävästi eri tavoilla riippuen renderöinnin tai fysiikan "
+"päivitystaajuudesta.\n"
+"Harkitse skriptin prosessointisilmukan käyttöä Timer solmun hyvin alhaiseen "
+"odotusaikaan luottamisen sijaan."
#: scene/main/viewport.cpp
msgid ""
@@ -14489,12 +14495,12 @@ msgstr ""
"johonkin solmuun näkyväksi."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
msgstr ""
-"Näyttöruudun koko on oltava suurempi kuin 0, jotta mitään renderöidään."
+"Näyttöruudun koko on oltava suurempi tai yhtä suuri kuin kaksi pikseliä "
+"kummassakin suunnassa, jotta mitään renderöidään."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/editor/translations/fr.po b/editor/translations/fr.po
index ea1e5dd57e..872b22322e 100644
--- a/editor/translations/fr.po
+++ b/editor/translations/fr.po
@@ -35,7 +35,7 @@
# Rémi Verschelde <rverschelde@gmail.com>, 2016-2017.
# Robin Arys <robinarys@hotmail.com>, 2017.
# Roger BR <drai_kin@hotmail.com>, 2016.
-# salty64 <cedric.arrabie@univ-pau.fr>, 2018, 2020.
+# salty64 <cedric.arrabie@univ-pau.fr>, 2018, 2020, 2021.
# Thomas Baijot <thomasbaijot@gmail.com>, 2016, 2019.
# Tommy Melançon-Roy <tommel1234@hotmail.com>, 2017-2018.
# Willow <theotimefd@aol.com>, 2018.
@@ -49,7 +49,7 @@
# Brice Lobet <tempo.data@gmail.com>, 2018.
# Florent Wijanto <f_wijanto@hotmail.com>, 2018.
# Olivier gareau <olivier.gareau@protonmail.com>, 2018.
-# Rémi Verschelde <akien@godotengine.org>, 2018, 2019, 2020.
+# Rémi Verschelde <akien@godotengine.org>, 2018, 2019, 2020, 2021.
# Rémi Bintein <reminus5@hotmail.fr>, 2018, 2019.
# Sylvain Corsini <sylvain.corsini@gmail.com>, 2018.
# Caye Pierre <pierrecaye@laposte.net>, 2019.
@@ -83,13 +83,14 @@
# Clément Topy <topy72.mine@gmail.com>, 2021.
# Cold <coldragon78@gmail.com>, 2021.
# Blackiris <divjvc@free.fr>, 2021.
+# Olivier Monnom <olivier.monnom@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-10-04 13:31+0000\n"
-"Last-Translator: Pierre Caye <pierrecaye@laposte.net>\n"
+"PO-Revision-Date: 2021-10-23 12:20+0200\n"
+"Last-Translator: salty64 <cedric.arrabie@univ-pau.fr>\n"
"Language-Team: French <https://hosted.weblate.org/projects/godot-engine/"
"godot/fr/>\n"
"Language: fr\n"
@@ -2500,9 +2501,8 @@ msgstr ""
"n'ont sans doute pas pu être satisfaites."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Impossible de démarrer le sous-processus !"
+msgstr "Impossible de sauver la (les) scènes(s) !"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2651,7 +2651,7 @@ msgstr "Sauvegarder les modifications effectuées à « %s » avant de quitt
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "%s n'existe plus ! Veuillez spécifier un nouvel endroit de sauvegarde."
#: editor/editor_node.cpp
msgid ""
@@ -2699,29 +2699,27 @@ msgstr "La scène actuelle n'est pas enregistrée. Ouvrir quand même ?"
#: editor/editor_node.cpp
msgid "Can't undo while mouse buttons are pressed."
-msgstr ""
+msgstr "Impossible d'annuler quand les boutons de la souris sont activés."
#: editor/editor_node.cpp
msgid "Nothing to undo."
-msgstr ""
+msgstr "Rien à annuler."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Undo: %s"
-msgstr "Annuler"
+msgstr "Annuler %s"
#: editor/editor_node.cpp
msgid "Can't redo while mouse buttons are pressed."
-msgstr ""
+msgstr "Impossible de rétablir quand les boutons de la souris sont activés."
#: editor/editor_node.cpp
msgid "Nothing to redo."
-msgstr ""
+msgstr "Rien à rétablir."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Redo: %s"
-msgstr "Refaire"
+msgstr "Refaire %s"
#: editor/editor_node.cpp
msgid "Can't reload a scene that was never saved."
@@ -2757,7 +2755,7 @@ msgstr "Quitter l'éditeur ?"
#: editor/editor_node.cpp
msgid "Open Project Manager?"
-msgstr "Ouvrir gestionnaire de projets ?"
+msgstr "Ouvrir le gestionnaire de projets ?"
#: editor/editor_node.cpp
msgid "Save & Quit"
@@ -3418,9 +3416,8 @@ msgid "Merge With Existing"
msgstr "Fusionner avec l'existant"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Apply MeshInstance Transforms"
-msgstr "Changer la transformation de l’animation"
+msgstr "Appliquer la transformation du MeshInstance"
#: editor/editor_node.cpp
msgid "Open & Run a Script"
@@ -3683,7 +3680,7 @@ msgstr ""
#: editor/editor_resource_picker.cpp
msgid "Quick Load"
-msgstr ""
+msgstr "Chargement rapide"
#: editor/editor_resource_picker.cpp editor/property_editor.cpp
msgid "Make Unique"
@@ -4519,6 +4516,12 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Vous avez des changements en attente qui n'ont pas encore été appliqués. "
+"Cliquez sur réimporter pour appliquer les changements des options "
+"d'importation\n"
+"Sélectionner une autre ressource dans le \"FileSystem dock\" sans cliquer "
+"sur réimport avant va annuler les changements faits dans le dock "
+"d'importation."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -5599,7 +5602,7 @@ msgstr "Dernier"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "All"
-msgstr "All"
+msgstr "Tous"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Search templates, projects, and demos"
@@ -5812,15 +5815,13 @@ msgstr "Déplacer le CanvasItem « %s » vers (%d, %d)"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Locked"
-msgstr "Verrouillage Sélectionné"
+msgstr "Verrouillé"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Grouped"
-msgstr "Groupes"
+msgstr "Groupé"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid ""
@@ -6773,14 +6774,12 @@ msgid "Remove Selected Item"
msgstr "Supprimer l'élément sélectionné"
#: editor/plugins/mesh_library_editor_plugin.cpp
-#, fuzzy
msgid "Import from Scene (Ignore Transforms)"
-msgstr "Importer depuis la scène"
+msgstr "Importer depuis la scène (Ignore les transformations)"
#: editor/plugins/mesh_library_editor_plugin.cpp
-#, fuzzy
msgid "Import from Scene (Apply Transforms)"
-msgstr "Importer depuis la scène"
+msgstr "Importer depuis la scène (Applique les transformations)"
#: editor/plugins/mesh_library_editor_plugin.cpp
msgid "Update from Scene"
@@ -7378,14 +7377,12 @@ msgid "Flip Portal"
msgstr "Retourner le Portal"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Occluder Set Transform"
-msgstr "Supprimer la transformation"
+msgstr "Définir la transformation pour l'occulteur"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Center Node"
-msgstr "Créer un nœud"
+msgstr "Centrer le nœud"
#: editor/plugins/root_motion_editor_plugin.cpp
msgid "AnimationTree has no path set to an AnimationPlayer"
@@ -7521,12 +7518,10 @@ msgid "Move Down"
msgstr "Déplacer vers le bas"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Script suivant"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Script précédent"
@@ -7893,14 +7888,12 @@ msgid "Skeleton2D"
msgstr "Squelette 2D"
#: editor/plugins/skeleton_2d_editor_plugin.cpp
-#, fuzzy
msgid "Reset to Rest Pose"
-msgstr "Assigner les os à la position de repos"
+msgstr "Remettre à la position de repos"
#: editor/plugins/skeleton_2d_editor_plugin.cpp
-#, fuzzy
msgid "Overwrite Rest Pose"
-msgstr "Écraser"
+msgstr "Écraser la position de repos"
#: editor/plugins/skeleton_editor_plugin.cpp
msgid "Create physical bones"
@@ -7927,74 +7920,62 @@ msgid "Perspective"
msgstr "Perspective"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Top Orthogonal"
-msgstr "Orthogonale"
+msgstr "Haut Orthogonal"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Top Perspective"
-msgstr "Perspective"
+msgstr "Perspective haute"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Bottom Orthogonal"
-msgstr "Orthogonale"
+msgstr "Bas Orthogonal"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Bottom Perspective"
-msgstr "Perspective"
+msgstr "Perspective basse"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Orthogonal"
-msgstr "Orthogonale"
+msgstr "Gauche Orthogonal"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspective"
+msgstr "Perspective Gauche"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Right Orthogonal"
-msgstr "Orthogonale"
+msgstr "Orthogonal Droit"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Right Perspective"
-msgstr "Perspective"
+msgstr "Perspective Droite"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Front Orthogonal"
-msgstr "Orthogonale"
+msgstr "Orthogonal avant"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Front Perspective"
-msgstr "Perspective"
+msgstr "Perspective Avant"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Rear Orthogonal"
-msgstr "Orthogonale"
+msgstr "Orthogonale arrière"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Rear Perspective"
-msgstr "Perspective"
+msgstr "Perspective arrière"
#. TRANSLATORS: This will be appended to the view name when Auto Orthogonal is enabled.
#: editor/plugins/spatial_editor_plugin.cpp
msgid " [auto]"
-msgstr ""
+msgstr " [auto]"
#. TRANSLATORS: This will be appended to the view name when Portal Occulusion is enabled.
#: editor/plugins/spatial_editor_plugin.cpp
msgid " [portals active]"
-msgstr ""
+msgstr " [portails actifs]"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Aborted."
@@ -8322,24 +8303,23 @@ msgstr "Vue de droite"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Vue de l'orbite vers le bas"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Vue de l'orbite vers la gauche"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Vue de l'orbite vers la droite"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Vue de devant"
+msgstr "Vue de l'orbite vers le devant"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Vue de l'orbite à 180°"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -8415,9 +8395,8 @@ msgid "View Portal Culling"
msgstr "Afficher le Portal culling"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View Occlusion Culling"
-msgstr "Afficher le Portal culling"
+msgstr "Voir la suppression de l'occlusion"
#: editor/plugins/spatial_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
@@ -8485,9 +8464,8 @@ msgid "Post"
msgstr "Post"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Unnamed Gizmo"
-msgstr "Projet sans titre"
+msgstr "Gizmo sans nom"
#: editor/plugins/sprite_editor_plugin.cpp
msgid "Create Mesh2D"
@@ -12648,14 +12626,12 @@ msgid "Set Portal Point Position"
msgstr "Définir la position du point du Portal"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Radius"
-msgstr "Changer le rayon de la forme du cylindre"
+msgstr "Définir le rayon de la sphère de l'occulteur"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Position"
-msgstr "Définir position d'entrée de la courbe"
+msgstr "Définir la position de la sphère de l'occulteur"
#: modules/csg/csg_gizmos.cpp
msgid "Change Cylinder Radius"
@@ -12943,9 +12919,8 @@ msgid "Class name can't be a reserved keyword"
msgstr "Le nom de classe ne peut pas être un mot-clé réservé"
#: modules/mono/csharp_script.cpp
-#, fuzzy
msgid "Build Solution"
-msgstr "Remplir la sélection"
+msgstr "Compiler la solution"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
@@ -14325,11 +14300,11 @@ msgstr ""
#: scene/3d/occluder.cpp
msgid "No shape is set."
-msgstr ""
+msgstr "Aucune forme n'est définie."
#: scene/3d/occluder.cpp
msgid "Only uniform scales are supported."
-msgstr ""
+msgstr "Seules les échelles uniformes sont prises en charge."
#: scene/3d/particles.cpp
msgid ""
@@ -14693,6 +14668,10 @@ msgid ""
"The GLES2 backend is currently in use, so these modes will act like Stretch "
"instead."
msgstr ""
+"Les options Tile et Tile Fit pour les propriétés d'extension d'axe ne sont "
+"efficaces que si vous utilisez le rendu GLES3.\n"
+"Le rendu GLES2 étant actuellement utilisé, ces modes se comporteront comme "
+"un étirement."
#: scene/gui/popup.cpp
msgid ""
@@ -14739,6 +14718,10 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Les temps d'attente très faibles des timers(< 0,05 seconde) peuvent se "
+"comporter de manière très différente selon la vitesse du rendu ou physique.\n"
+"Envisagez d'utiliser la boucle de traitement d'un script au lieu de vous "
+"fier à un timer pour les temps d'attente très faibles."
#: scene/main/viewport.cpp
msgid ""
@@ -14753,17 +14736,16 @@ msgstr ""
"RenderTarget et assignez sa texture à un nœud pouvant l'afficher."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
msgstr ""
-"La taille de la fenêtre d'affichage doit être supérieure à 0 pour pouvoir "
-"afficher quoi que ce soit."
+"La taille de la fenêtre d'affichage doit être supérieure ou égale à 2 pixels "
+"dans les deux sens pour que le rendu soit possible."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
-msgstr ""
+msgstr "Définir les sphères pour OccluderShapeSphere"
#: scene/resources/visual_shader_nodes.cpp
msgid ""
diff --git a/editor/translations/he.po b/editor/translations/he.po
index 830033a726..9e41b7f9c4 100644
--- a/editor/translations/he.po
+++ b/editor/translations/he.po
@@ -966,7 +966,7 @@ msgstr "מעבר למתודה"
#: editor/create_dialog.cpp
msgid "Change %s Type"
-msgstr "שנה ×ת הסוג של s%"
+msgstr "שנה ×ת הסוג של %s"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
msgid "Change"
@@ -1026,7 +1026,7 @@ msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will only take effect when reloaded."
msgstr ""
-"סצנה 's%' נמצ×ת כרגע בעריכה.\n"
+"סצנה '%s' נמצ×ת כרגע בעריכה.\n"
"×©×™× ×•×™×™× ×™×›× ×¡×• לתוקף בטעינה מחדש."
#: editor/dependency_editor.cpp
@@ -1034,7 +1034,7 @@ msgid ""
"Resource '%s' is in use.\n"
"Changes will only take effect when reloaded."
msgstr ""
-"מש×ב 's%' × ×ž×¦× ×‘×©×™×ž×•×©.\n"
+"מש×ב '%s' × ×ž×¦× ×‘×©×™×ž×•×©.\n"
"×©×™× ×•×™×™× ×™×›× ×¡×• לתוקף רק בטעינה מחדש."
#: editor/dependency_editor.cpp
diff --git a/editor/translations/ja.po b/editor/translations/ja.po
index 1eb457926d..4a2075e20b 100644
--- a/editor/translations/ja.po
+++ b/editor/translations/ja.po
@@ -40,7 +40,7 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-10-15 04:33+0000\n"
+"PO-Revision-Date: 2021-10-22 06:04+0000\n"
"Last-Translator: nitenook <admin@alterbaum.net>\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/"
"godot/ja/>\n"
@@ -2431,9 +2431,8 @@ msgstr ""
"満ãŸã›ã¾ã›ã‚“ã§ã—ãŸã€‚"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "変æ›ã—ãŸãƒ†ã‚¯ã‚¹ãƒãƒ£ã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ:"
+msgstr "一ã¤ã¾ãŸã¯è¤‡æ•°ã®ã‚·ãƒ¼ãƒ³ã‚’ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸï¼"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2576,7 +2575,7 @@ msgstr "é–‰ã˜ã‚‹å‰ã«ã€'%s' ã¸ã®å¤‰æ›´ã‚’ä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "%s ã¯å­˜åœ¨ã—ãªããªã‚Šã¾ã—ãŸï¼æ–°ã—ã„ä¿å­˜å…ˆã‚’指定ã—ã¦ãã ã•ã„。"
#: editor/editor_node.cpp
msgid ""
@@ -3324,7 +3323,7 @@ msgstr "ライブラリã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
#: editor/editor_node.cpp
msgid "Merge With Existing"
-msgstr "既存ã®(ライブラリを)マージ"
+msgstr "既存ã®ã‚‚ã®ã¨ãƒžãƒ¼ã‚¸ã™ã‚‹"
#: editor/editor_node.cpp
msgid "Apply MeshInstance Transforms"
@@ -4416,6 +4415,10 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"ã¾ã é©ç”¨ã•れã¦ã„ãªã„ä¿ç•™ä¸­ã®å¤‰æ›´ãŒã‚りã¾ã™ã€‚å†ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’クリックã™ã‚‹ã¨ã€ã‚¤"
+"ンãƒãƒ¼ãƒˆã®ã‚ªãƒ—ションã«åŠ ãˆãŸå¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã€‚\n"
+"å†ã‚¤ãƒ³ãƒãƒ¼ãƒˆã‚’クリックã›ãšã«ãƒ•ァイルシステム ドックã‹ã‚‰ä»–ã®ãƒªã‚½ãƒ¼ã‚¹ã‚’é¸æŠžã™ã‚‹"
+"ã¨ã€ã‚¤ãƒ³ãƒãƒ¼ãƒˆã®ãƒ‰ãƒƒã‚¯ã§åŠ ãˆãŸå¤‰æ›´ã¯ç ´æ£„ã•れã¾ã™ã€‚"
#: editor/import_dock.cpp
msgid "Import As:"
@@ -6570,14 +6573,14 @@ msgid "Create Multiple Convex Collision Siblings"
msgstr "複数ã®å‡¸åž‹ã‚³ãƒªã‚¸ãƒ§ãƒ³ã®å…„弟を作æˆ"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid ""
"Creates a polygon-based collision shape.\n"
"This is a performance middle-ground between a single convex collision and a "
"polygon-based collision."
msgstr ""
"ãƒãƒªã‚´ãƒ³ãƒ™ãƒ¼ã‚¹ã®ã‚³ãƒªã‚¸ãƒ§ãƒ³ã‚·ã‚§ã‚¤ãƒ—を作æˆã—ã¾ã™ã€‚\n"
-"ã“れã¯ã€ä¸Šè¨˜ã®2ã¤ã®ã‚ªãƒ—ションã®ä¸­é–“çš„ãªãƒ‘フォーマンスã§ã™ã€‚"
+"ã“れã¯ã€å˜ä¸€ã®å‡¸åž‹ã‚³ãƒªã‚¸ãƒ§ãƒ³ã¨ãƒãƒªã‚´ãƒ³ãƒ™ãƒ¼ã‚¹ã®ã‚³ãƒªã‚¸ãƒ§ãƒ³ã®ä¸­é–“çš„ãªãƒ‘フォーマ"
+"ンスã§ã™ã€‚"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Outline Mesh..."
@@ -6708,7 +6711,7 @@ msgstr "サーフェスを投入ã™ã‚‹"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate MultiMesh"
-msgstr "マルãƒãƒ¡ãƒƒã‚·ãƒ¥ã‚’投入ã™ã‚‹"
+msgstr "MultiMeshを投入ã™ã‚‹"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Target Surface:"
@@ -7228,28 +7231,24 @@ msgid "Flip Portals"
msgstr "ãƒãƒ¼ã‚¿ãƒ«ã‚’å転"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Room Generate Points"
-msgstr "生æˆã—ãŸãƒã‚¤ãƒ³ãƒˆã®æ•°:"
+msgstr "Roomã®ãƒã‚¤ãƒ³ãƒˆã‚’生æˆ"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Generate Points"
-msgstr "生æˆã—ãŸãƒã‚¤ãƒ³ãƒˆã®æ•°:"
+msgstr "ãƒã‚¤ãƒ³ãƒˆã‚’生æˆ"
#: editor/plugins/room_manager_editor_plugin.cpp
msgid "Flip Portal"
msgstr "ãƒãƒ¼ã‚¿ãƒ«ã‚’å転"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Occluder Set Transform"
-msgstr "トランスフォームをクリア"
+msgstr "オクルーダーã®ãƒˆãƒ©ãƒ³ã‚¹ãƒ•ォームをセット"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Center Node"
-msgstr "ノードを生æˆ"
+msgstr "中央ノード"
#: editor/plugins/root_motion_editor_plugin.cpp
msgid "AnimationTree has no path set to an AnimationPlayer"
@@ -7386,14 +7385,12 @@ msgid "Move Down"
msgstr "下ã«ç§»å‹•"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "次ã®ã‚¹ã‚¯ãƒªãƒ—ト"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
-msgstr "ç›´å‰ã®ã‚¹ã‚¯ãƒªãƒ—ト"
+msgstr "å‰ã®ã‚¹ã‚¯ãƒªãƒ—ト"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
@@ -7810,9 +7807,8 @@ msgid "Left Orthogonal"
msgstr "å·¦å´é¢ 平行投影"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "é€è¦–投影"
+msgstr "å·¦å´é¢ é€è¦–投影"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8103,9 +8099,8 @@ msgstr ""
"ゲーム内ã®ãƒ‘フォーマンスを確実ã«ç¤ºã™ã‚‚ã®ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Convert Rooms"
-msgstr "%s ã«å¤‰æ›"
+msgstr "Roomを変æ›"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
@@ -8143,7 +8138,7 @@ msgstr "スナップを使用"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Converts rooms for portal culling."
-msgstr ""
+msgstr "ãƒãƒ¼ã‚¿ãƒ«ã‚«ãƒªãƒ³ã‚°ç”¨ã«Roomを変æ›ã—ã¾ã™ã€‚"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
@@ -8171,24 +8166,23 @@ msgstr "å³å´é¢å›³"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "オービットビュー 下"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "オービットビュー 左"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "オービットビュー å³"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "å‰é¢å›³"
+msgstr "オービットビュー 上"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "オービットビュー 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -8673,9 +8667,8 @@ msgid "With Data"
msgstr "データ付"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Select by data type:"
-msgstr "ãƒŽãƒ¼ãƒ‰ã‚’é¸æŠž"
+msgstr "データã®ã‚¿ã‚¤ãƒ—ã‹ã‚‰é¸æŠž:"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all visible color items."
@@ -8747,11 +8740,11 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Collapse types."
-msgstr "タイプを折りãŸãŸã‚€"
+msgstr "タイプを折りãŸãŸã‚€ã€‚"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Expand types."
-msgstr "タイプを展開"
+msgstr "タイプを展開。"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Select all Theme items."
@@ -8774,9 +8767,8 @@ msgid "Deselect all Theme items."
msgstr "ã™ã¹ã¦ã®ãƒ†ãƒ¼ãƒžã‚¢ã‚¤ãƒ†ãƒ ã®é¸æŠžã‚’解除ã™ã‚‹ã€‚"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Import Selected"
-msgstr "シーンをインãƒãƒ¼ãƒˆ"
+msgstr "é¸æŠžã•れãŸã‚‚ã®ã‚’インãƒãƒ¼ãƒˆ"
#: editor/plugins/theme_editor_plugin.cpp
msgid ""
@@ -8970,19 +8962,16 @@ msgstr ""
"ã¦ã®ã“ã®ã‚¿ã‚¤ãƒ—ã®StyleBoxã§åŒã˜ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ãŒæ›´æ–°ã•れã¾ã™ã€‚"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Add Type"
-msgstr "タイプ(型)"
+msgstr "タイプを追加"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Add Item Type"
-msgstr "アイテムを追加"
+msgstr "アイテムã®ã‚¿ã‚¤ãƒ—を追加"
#: editor/plugins/theme_editor_plugin.cpp
-#, fuzzy
msgid "Node Types:"
-msgstr "ノードタイプ"
+msgstr "ノードã®ã‚¿ã‚¤ãƒ—:"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Show Default"
@@ -9030,6 +9019,8 @@ msgid ""
"Toggle the control picker, allowing to visually select control types for "
"edit."
msgstr ""
+"コントロールピッカーを切り替ãˆã¦ã€ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ã‚¿ã‚¤ãƒ—を視覚的ã«é¸æŠžã—ã¦ç·¨é›†ã§"
+"ãるよã†ã«ã—ã¾ã™ã€‚"
#: editor/plugins/theme_editor_preview.cpp
msgid "Toggle Button"
@@ -9064,9 +9055,8 @@ msgid "Checked Radio Item"
msgstr "ãƒã‚§ãƒƒã‚¯æ¸ˆã¿ãƒ©ã‚¸ã‚ª アイテム"
#: editor/plugins/theme_editor_preview.cpp
-#, fuzzy
msgid "Named Separator"
-msgstr "åå‰ä»˜ã分類。"
+msgstr "åå‰ä»˜ãセパレーター"
#: editor/plugins/theme_editor_preview.cpp
msgid "Submenu"
@@ -11306,22 +11296,20 @@ msgid "Remove Translation"
msgstr "翻訳を除去"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Translation Resource Remap: Add %d Path(s)"
-msgstr "リソースå†ãƒžãƒƒãƒ—ãŒå†ãƒžãƒƒãƒ—を追加"
+msgstr "翻訳リソースã®å†ãƒžãƒƒãƒ—: %d個ã®ãƒ‘スを追加"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Translation Resource Remap: Add %d Remap(s)"
-msgstr "リソースå†ãƒžãƒƒãƒ—ãŒå†ãƒžãƒƒãƒ—を追加"
+msgstr "翻訳リソースã®å†ãƒžãƒƒãƒ—: %d個ã®å†ãƒžãƒƒãƒ—を追加"
#: editor/project_settings_editor.cpp
msgid "Change Resource Remap Language"
-msgstr "リソースリマップ言語を変更"
+msgstr "リソースã®å†ãƒžãƒƒãƒ—言語を変更"
#: editor/project_settings_editor.cpp
msgid "Remove Resource Remap"
-msgstr "リソースã®ãƒªãƒžãƒƒãƒ—を削除"
+msgstr "リソースã®å†ãƒžãƒƒãƒ—を削除"
#: editor/project_settings_editor.cpp
msgid "Remove Resource Remap Option"
@@ -11389,7 +11377,7 @@ msgstr "翻訳:"
#: editor/project_settings_editor.cpp
msgid "Remaps"
-msgstr "リマップ"
+msgstr "å†ãƒžãƒƒãƒ—"
#: editor/project_settings_editor.cpp
msgid "Resources:"
@@ -11397,7 +11385,7 @@ msgstr "リソース:"
#: editor/project_settings_editor.cpp
msgid "Remaps by Locale:"
-msgstr "ロケールã«å¾“ã„リマップ:"
+msgstr "ロケールã«å¾“ã„å†ãƒžãƒƒãƒ—:"
#: editor/project_settings_editor.cpp
msgid "Locale"
@@ -12456,14 +12444,12 @@ msgid "Set Portal Point Position"
msgstr "Portal ãƒã‚¤ãƒ³ãƒˆã®ä½ç½®ã‚’設定"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Radius"
-msgstr "円柱シェイプã®åŠå¾„を変更"
+msgstr "オクルーダーã®çƒå½¢ã®åŠå¾„をセット"
#: editor/spatial_editor_gizmos.cpp
-#, fuzzy
msgid "Set Occluder Sphere Position"
-msgstr "曲線ã®In-Controlã®ä½ç½®ã‚’指定"
+msgstr "オクルーダーã®çƒå½¢ã®ä½ç½®ã‚’セット"
#: modules/csg/csg_gizmos.cpp
msgid "Change Cylinder Radius"
@@ -12850,7 +12836,7 @@ msgstr ""
#: modules/visual_script/visual_script.cpp
msgid "Stack overflow with stack depth: "
-msgstr "スタックオーãƒãƒ¼ãƒ•ロー ã‚¹ã‚¿ãƒƒã‚¯ã®æ·±ã•: "
+msgstr "スタックオーãƒãƒ¼ãƒ•ロー発生 ã‚¹ã‚¿ãƒƒã‚¯ã®æ·±ã•: "
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Signal Arguments"
@@ -13089,9 +13075,8 @@ msgid "Select at least one node with sequence port."
msgstr "シーケンス ãƒãƒ¼ãƒˆã‚’æŒã¤ãƒŽãƒ¼ãƒ‰ã‚’å°‘ãªãã¨ã‚‚ 1 ã¤é¸æŠžã—ã¾ã™ã€‚"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Try to only have one sequence input in selection."
-msgstr "セクションã§ã¯å”¯ä¸€ã¤ã®ã‚·ãƒ¼ã‚±ãƒ³ã‚¹å…¥åŠ›ã‚’æŒã¤ã‚ˆã†ã«ã—ã¦ãã ã•ã„。"
+msgstr "é¸æŠžã™ã‚‹ã‚·ãƒ¼ã‚±ãƒ³ã‚¹å…¥åŠ›ã¯1ã¤ã ã‘ã«ã—ã¦ãã ã•ã„。"
#: modules/visual_script/visual_script_editor.cpp
msgid "Create Function"
@@ -13187,7 +13172,7 @@ msgstr "イテレーターãŒç„¡åйã«ãªã‚Šã¾ã—ãŸ: "
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name."
-msgstr "インデックスプロパティåãŒç„¡åйã§ã™ã€‚"
+msgstr "インデックスã®ãƒ—ロパティåãŒç„¡åйã§ã™ã€‚"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Base object is not a Node!"
@@ -13473,7 +13458,7 @@ msgstr ""
msgid ""
"Unable to overwrite res://android/build/res/*.xml files with project name"
msgstr ""
-"res://android/build/res/*.xml ファイルをプロジェクトåã§ä¸Šæ›¸ãã§ãã¾ã›ã‚“。"
+"res://android/build/res/*.xml ファイルをプロジェクトåã§ä¸Šæ›¸ãã§ãã¾ã›ã‚“"
#: platform/android/export/export_plugin.cpp
msgid "Could not export project files to gradle project\n"
@@ -13855,8 +13840,8 @@ msgid ""
"A material to process the particles is not assigned, so no behavior is "
"imprinted."
msgstr ""
-"パーティクルを処ç†ã™ã‚‹ãƒžãƒ†ãƒªã‚¢ãƒ«ã¯å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ãªã„ãŸã‚ã€å‹•作ã¯ã‚¤ãƒ³ãƒ—リン"
-"トã•れã¾ã›ã‚“。"
+"パーティクルを処ç†ã™ã‚‹ãƒžãƒ†ãƒªã‚¢ãƒ«ãŒå‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ãªã„ãŸã‚ã€å‹•ä½œã‚’åæ˜ ã§ãã¾"
+"ã›ã‚“ã§ã—ãŸã€‚"
#: scene/2d/particles_2d.cpp
msgid ""
@@ -13966,9 +13951,8 @@ msgid "Preparing environment"
msgstr "環境を準備中"
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "Generating capture"
-msgstr "ライトマップã®ç”Ÿæˆ"
+msgstr "キャプãƒãƒ£ãƒ¼ã‚’生æˆä¸­"
#: scene/3d/baked_lightmap.cpp
msgid "Saving lightmaps"
@@ -14203,6 +14187,8 @@ msgid ""
"Room convex hull contains a large number of planes.\n"
"Consider simplifying the room bound in order to increase performance."
msgstr ""
+"Roomã®å‡¸åŒ…ã«å¤§é‡ã®å¹³é¢ãŒå«ã¾ã‚Œã¦ã„ã¾ã™ã€‚\n"
+"パフォーマンスã®å‘上ã®ãŸã‚ã«ã€Roomã®å¢ƒç•Œã®å˜ç´”化を検討ã—ã¦ãã ã•ã„。"
#: scene/3d/room_group.cpp
msgid "The RoomManager should not be placed inside a RoomGroup."
@@ -14449,6 +14435,10 @@ msgid ""
"The GLES2 backend is currently in use, so these modes will act like Stretch "
"instead."
msgstr ""
+"Axis Stretchプロパティã®TileãŠã‚ˆã³Tile Fitオプションã¯ã€GLES3レンダリングãƒãƒƒ"
+"クエンドを使用ã—ã¦ã„ã‚‹å ´åˆã«ã®ã¿æœ‰åйã§ã™ã€‚\n"
+"ç¾åœ¨GLES2ãƒãƒƒã‚¯ã‚¨ãƒ³ãƒ‰ãŒä½¿ç”¨ã•れã¦ã„ã‚‹ãŸã‚ã€ã“れらã®ãƒ¢ãƒ¼ãƒ‰ã¯ä»£ã‚りã«Stretchã®"
+"よã†ã«æŒ¯ã‚‹èˆžã„ã¾ã™ã€‚"
#: scene/gui/popup.cpp
msgid ""
@@ -14493,6 +14483,10 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"タイマーã®å¾…ã¡æ™‚é–“ãŒéžå¸¸ã«çŸ­ã„ (0.05秒未満) ã®å ´åˆã€æç”»ã¾ãŸã¯ç‰©ç†ãƒ•レーム"
+"レートã«å¿œã˜ã¦å¤§å¹…ã«å‹•作ãŒç•°ãªã‚‹å¯èƒ½æ€§ãŒã‚りã¾ã™ã€‚\n"
+"éžå¸¸ã«çŸ­ã„å¾…ã¡æ™‚é–“ã®å ´åˆã€Timerを使用ã›ãšã«ã‚¹ã‚¯ãƒªãƒ—トã®processループを使用ã™"
+"ã‚‹ã“ã¨ã‚’検討ã—ã¦ãã ã•ã„。"
#: scene/main/viewport.cpp
msgid ""
@@ -14507,15 +14501,15 @@ msgstr ""
"当ã¦ã¾ã™ã€‚"
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
-msgstr "レンダーã™ã‚‹ã«ã¯ãƒ“ューãƒãƒ¼ãƒˆã®ã‚µã‚¤ã‚ºãŒ 0 より大ãã„å¿…è¦ãŒã‚りã¾ã™ã€‚"
+msgstr ""
+"レンダーã™ã‚‹ã«ã¯Viewportã®ç¸¦æ¨ªãれãžã‚ŒãŒ2ピクセル以上ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚"
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
-msgstr ""
+msgstr "OccluderShapeSphereã®çƒå½¢ã‚’セット"
#: scene/resources/visual_shader_nodes.cpp
msgid ""
@@ -14559,7 +14553,7 @@ msgstr ""
#: servers/visual/shader_language.cpp
msgid "Fragment-stage varying could not been accessed in custom function!"
-msgstr ""
+msgstr "カスタム関数内ã§Fragment-stageã®Varyingã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“ã§ã—ãŸï¼"
#: servers/visual/shader_language.cpp
msgid "Assignment to function."
diff --git a/editor/translations/pt.po b/editor/translations/pt.po
index 008100444d..e78f560111 100644
--- a/editor/translations/pt.po
+++ b/editor/translations/pt.po
@@ -24,7 +24,7 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-09-29 02:21+0000\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
"Last-Translator: João Lopes <linux-man@hotmail.com>\n"
"Language-Team: Portuguese <https://hosted.weblate.org/projects/godot-engine/"
"godot/pt/>\n"
@@ -2417,9 +2417,8 @@ msgstr ""
"heranças) não puderam ser satisfeitas."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Não consegui iniciar o subprocesso!"
+msgstr "Incapaz de guardar uma ou mais cenas!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2563,7 +2562,7 @@ msgstr "Guardar alterações a '%s' antes de fechar?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "% não existe mais! Especifique uma nova localização para guardar."
#: editor/editor_node.cpp
msgid ""
@@ -4410,6 +4409,10 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"Há alterações pendentes que ainda não foram aplicadas. Clique Reimportar "
+"para aplicar alterações feitas nas opções de importação.\n"
+"Se selecionar outro recurso na doca FileSystem sem Reimportar primeiro, "
+"serão rejeitadas alterações feitas na doca Import."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7376,14 +7379,12 @@ msgid "Move Down"
msgstr "Mover para baixo"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Próximo Script"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
-msgstr "Script anterior"
+msgstr "Script Anterior"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
@@ -7796,9 +7797,8 @@ msgid "Left Orthogonal"
msgstr "Ortogonal Esquerda"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "Perspetiva"
+msgstr "Perspetiva Esquerda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8157,24 +8157,23 @@ msgstr "Vista Direita"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Vista Órbita Baixo"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Vista Órbita Esquerda"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Vista Órbita Direita"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Vista de Frente"
+msgstr "Vista de Órbita Cima"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Vista Órbita 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -9032,7 +9031,7 @@ msgstr "Item Desativado"
#: editor/plugins/theme_editor_preview.cpp
msgid "Check Item"
-msgstr "Verificar item"
+msgstr "Marcar item"
#: editor/plugins/theme_editor_preview.cpp
msgid "Checked Item"
@@ -11757,6 +11756,10 @@ msgid ""
"FileSystem dock context menu\n"
"or create an inherited scene using Scene > New Inherited Scene... instead."
msgstr ""
+"Incapaz de guardar o ramo do nó raiz como uma cena instanciada.\n"
+"Para criar uma cópia editável da cena atual, duplique-a usando o menu de "
+"contexto da doca FileSystem\n"
+"ou crie uma cena herdada usando Cena > Nova Cena Herdada..."
#: editor/scene_tree_dock.cpp
msgid ""
@@ -11764,6 +11767,9 @@ msgid ""
"To create a variation of a scene, you can make an inherited scene based on "
"the instanced scene using Scene > New Inherited Scene... instead."
msgstr ""
+"Incapaz de guardar o ramo de uma cena já instanciada.\n"
+"Para criar a variação de uma cena, pode fazer uma cena herdada baseada na "
+"cena instanciada usando Cena > Nova Cena Herdada..."
#: editor/scene_tree_dock.cpp
msgid "Save New Scene As..."
@@ -13281,6 +13287,8 @@ msgid ""
"Either Debug Keystore, Debug User AND Debug Password settings must be "
"configured OR none of them."
msgstr ""
+"Tanto a Keystore de Depuração, Utilizador de Depuração E Senha de Depuração "
+"têm de ser configuradas OU nenhuma delas."
#: platform/android/export/export_plugin.cpp
msgid "Debug keystore not configured in the Editor Settings nor in the preset."
@@ -13293,11 +13301,13 @@ msgid ""
"Either Release Keystore, Release User AND Release Password settings must be "
"configured OR none of them."
msgstr ""
+"Tanto a Keystore de Lançamento, Utilizador de Lançamento E Senha de "
+"Lançamento têm de ser configuradas OU nenhuma delas."
#: platform/android/export/export_plugin.cpp
msgid "Release keystore incorrectly configured in the export preset."
msgstr ""
-"Lançamento de keystore configurado incorretamente na predefinição exportada."
+"Keystore de lançamento configurado incorretamente na predefinição exportada."
#: platform/android/export/export_plugin.cpp
msgid "A valid Android SDK path is required in Editor Settings."
@@ -13371,6 +13381,10 @@ msgid ""
"directory.\n"
"The resulting %s is unsigned."
msgstr ""
+"'apksigner' não foi encontrado.\n"
+"Verifique se o comando está disponível na diretoria Android SDK build-"
+"tools.\n"
+"O % resultante não está assinado."
#: platform/android/export/export_plugin.cpp
msgid "Signing debug %s..."
@@ -13500,6 +13514,10 @@ msgid ""
"Please build a template with all required libraries, or uncheck the missing "
"architectures in the export preset."
msgstr ""
+"Bibliotecas em falta no modelo de exportação para as arquiteturas "
+"selecionadas: %s.\n"
+"Construa um modelo com todas as bibliotecas necessárias, ou desmarque as "
+"arquiteturas em falta na predefinição de exportação."
#: platform/android/export/export_plugin.cpp
msgid "Adding files..."
@@ -13584,19 +13602,19 @@ msgstr "Identificador de pacote inválido:"
#: platform/osx/export/export.cpp
msgid "Notarization: code signing required."
-msgstr ""
+msgstr "Notarização: assinatura de código necessária."
#: platform/osx/export/export.cpp
msgid "Notarization: hardened runtime required."
-msgstr ""
+msgstr "Notarização: hardened runtime necessário."
#: platform/osx/export/export.cpp
msgid "Notarization: Apple ID name not specified."
-msgstr ""
+msgstr "Notarização: nome Apple ID não especificado."
#: platform/osx/export/export.cpp
msgid "Notarization: Apple ID password not specified."
-msgstr ""
+msgstr "Notarização: senha Apple ID não especificada."
#: platform/uwp/export/export.cpp
msgid "Invalid package short name."
@@ -14028,6 +14046,9 @@ msgid ""
"longer has any effect.\n"
"To remove this warning, disable the GIProbe's Compress property."
msgstr ""
+"A propriedade GIProbe Compress foi descontinuada devido a bugs conhecidos e "
+"não tem qualquer efeito.\n"
+"Para remover este aviso desative a propriedade Compress de GIProbe."
#: scene/3d/light.cpp
msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows."
@@ -14158,6 +14179,8 @@ msgid ""
"Room convex hull contains a large number of planes.\n"
"Consider simplifying the room bound in order to increase performance."
msgstr ""
+"O casco convexo do quarto contem um grande número de planos.\n"
+"Pense em simplificar os limites do quarto para melhorar o desempenho."
#: scene/3d/room_group.cpp
msgid "The RoomManager should not be placed inside a RoomGroup."
@@ -14176,6 +14199,8 @@ msgid ""
"Portal Depth Limit is set to Zero.\n"
"Only the Room that the Camera is in will render."
msgstr ""
+"Limite de Profundidade do Portal está definido como Zero.\n"
+"Só vai ser renderizado o quarto onde a Câmara está."
#: scene/3d/room_manager.cpp
msgid "There should only be one RoomManager in the SceneTree."
@@ -14186,6 +14211,8 @@ msgid ""
"RoomList path is invalid.\n"
"Please check the RoomList branch has been assigned in the RoomManager."
msgstr ""
+"Caminho do RoomList é inválido.\n"
+"Verifique se o ramo RoomList foi atribuído no RoomManager."
#: scene/3d/room_manager.cpp
msgid "RoomList contains no Rooms, aborting."
@@ -14290,7 +14317,7 @@ msgstr "Animação não encontrada: '%s'"
#: scene/animation/animation_player.cpp
msgid "Anim Apply Reset"
-msgstr ""
+msgstr "Anim Aplicar Reinicialização"
#: scene/animation/animation_tree.cpp
msgid "In node '%s', invalid animation: '%s'."
@@ -14398,6 +14425,9 @@ msgid ""
"The GLES2 backend is currently in use, so these modes will act like Stretch "
"instead."
msgstr ""
+"As opções Tile e Tile Fit para as propriedades Axis Stretch só são efetivas "
+"na renderização GLES3.\n"
+"Como GLES2 está a ser usado atualmente, estes modos funcionarão como Stretch."
#: scene/gui/popup.cpp
msgid ""
@@ -14442,6 +14472,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Tempos de espera do Timer muito baixos (< 0.05 segundos) podem originar "
+"comportamentos muito diferentes dependendo do renderizador ou da taxa de "
+"frames física.\n"
+"Considere usar um ciclo de processo de script em vez de depender de um Timer "
+"para tempos de espera muito baixos."
#: scene/main/viewport.cpp
msgid ""
@@ -14456,11 +14491,12 @@ msgstr ""
"RenderTarget e atribua a sua textura interna a outro nó para visualizar."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
-msgstr "O tamanho do viewport tem de ser maior do que 0 para renderizar."
+msgstr ""
+"O tamanho do Viewport tem de ser maior ou igual a 2 pixeis em ambas as "
+"dimensões para renderizar."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
@@ -14495,16 +14531,20 @@ msgid ""
"Varyings which assigned in 'vertex' function may not be reassigned in "
"'fragment' or 'light'."
msgstr ""
+"Variantes atribuídas na função 'vertex' não podem ser reatribuídas em "
+"'fragment' ou 'light'."
#: servers/visual/shader_language.cpp
msgid ""
"Varyings which assigned in 'fragment' function may not be reassigned in "
"'vertex' or 'light'."
msgstr ""
+"Variantes atribuídas na função 'fragment' não podem ser reatribuídas em "
+"'vertex' ou 'light'."
#: servers/visual/shader_language.cpp
msgid "Fragment-stage varying could not been accessed in custom function!"
-msgstr ""
+msgstr "Variante fragment-stage não pode ser acedida em função personalizada!"
#: servers/visual/shader_language.cpp
msgid "Assignment to function."
diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po
index 804740bf4a..9e7d53c52e 100644
--- a/editor/translations/pt_BR.po
+++ b/editor/translations/pt_BR.po
@@ -124,13 +124,14 @@
# William Weber Berrutti <wwberrutti@protonmail.ch>, 2021.
# Zenvasca <zenvasca@gmail.com>, 2021.
# Wladimir Roberto Barbosa <wladrbarbosa@gmail.com>, 2021.
+# Mário Victor Ribeiro Silva <mariovictorrs@gmail.com>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: 2016-05-30\n"
-"PO-Revision-Date: 2021-10-09 16:52+0000\n"
-"Last-Translator: Wladimir Roberto Barbosa <wladrbarbosa@gmail.com>\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
+"Last-Translator: Mário Victor Ribeiro Silva <mariovictorrs@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"godot-engine/godot/pt_BR/>\n"
"Language: pt_BR\n"
@@ -2521,9 +2522,8 @@ msgstr ""
"herança) não foram satisfeitas."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Não se pôde salvar textura convertida:"
+msgstr "Não foi possível salvar um ou mais cenas!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2665,7 +2665,7 @@ msgstr "Salvar alterações em '%s' antes de fechar?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "%s não existe! Por favor especifique um novo local para salvar."
#: editor/editor_node.cpp
msgid ""
@@ -2720,9 +2720,8 @@ msgid "Nothing to undo."
msgstr "Nada para desfazer."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Undo: %s"
-msgstr "Desfazer"
+msgstr "Desfazer: %s"
#: editor/editor_node.cpp
msgid "Can't redo while mouse buttons are pressed."
@@ -2733,9 +2732,8 @@ msgid "Nothing to redo."
msgstr "Nada para refazer."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Redo: %s"
-msgstr "Refazer"
+msgstr "Refazer: %s"
#: editor/editor_node.cpp
msgid "Can't reload a scene that was never saved."
@@ -3374,9 +3372,8 @@ msgid "Install from file"
msgstr "Instalar do arquivo"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Select android sources file"
-msgstr "Selecione o arquivo de fontes do Android"
+msgstr "Selecione os arquivos fontes do Android"
#: editor/editor_node.cpp
msgid ""
@@ -3463,7 +3460,6 @@ msgid "Select"
msgstr "Selecionar"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Select Current"
msgstr "Selecione Atual"
@@ -3791,9 +3787,8 @@ msgid "Uninstall these templates."
msgstr "Desinstalar esses modelos."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "There are no mirrors available."
-msgstr "Não existe o arquivo '%s'."
+msgstr "Não há espelhos disponíveis."
#: editor/export_template_manager.cpp
#, fuzzy
@@ -3809,23 +3804,20 @@ msgid "Error requesting URL:"
msgstr "Erro ao solicitar URL:"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Connecting to the mirror..."
-msgstr "Conectando..."
+msgstr "Conectando ao espelho..."
#: editor/export_template_manager.cpp
msgid "Can't resolve the requested address."
msgstr "Não é possível resolver o endereço solicitado."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Can't connect to the mirror."
-msgstr "Não foi possível conectar ao host:"
+msgstr "Não foi possível conectar ao espelho."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "No response from the mirror."
-msgstr "Sem resposta do host:"
+msgstr "Sem resposta do espelho."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3978,9 +3970,8 @@ msgstr ""
"As exportações de modelos estão instaladas e prontas para serem usadas."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Open Folder"
-msgstr "Abrir um arquivo"
+msgstr "Abrir Pasta"
#: editor/export_template_manager.cpp
msgid "Open the folder containing installed templates for the current version."
@@ -3996,14 +3987,12 @@ msgid "Uninstall templates for the current version."
msgstr "Valor inicial para o contador"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Download from:"
-msgstr "Erro ao baixar"
+msgstr "Baixar de:"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Open in Web Browser"
-msgstr "Rodar no Navegador"
+msgstr "Abrir no Navegador"
#: editor/export_template_manager.cpp
#, fuzzy
@@ -4029,7 +4018,6 @@ msgstr ""
"desenvolvimento."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Install from File"
msgstr "Instalar a Partir do Arquivo"
@@ -4049,14 +4037,12 @@ msgid "Cancel the download of the templates."
msgstr "Não se pôde abrir zip dos modelos de exportação."
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Other Installed Versions:"
-msgstr "Versões Instaladas:"
+msgstr "Outras Versões Instaladas:"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "Uninstall Template"
-msgstr "Desinstalar"
+msgstr "Desinstalar Template"
#: editor/export_template_manager.cpp
msgid "Select Template File"
@@ -4576,14 +4562,12 @@ msgid "Failed to load resource."
msgstr "Falha ao carregar recurso."
#: editor/inspector_dock.cpp
-#, fuzzy
msgid "Copy Properties"
-msgstr "Propriedades"
+msgstr "Copiar Propriedades"
#: editor/inspector_dock.cpp
-#, fuzzy
msgid "Paste Properties"
-msgstr "Propriedades"
+msgstr "Colar Propriedades"
#: editor/inspector_dock.cpp
msgid "Make Sub-Resources Unique"
@@ -4639,9 +4623,8 @@ msgid "History of recently edited objects."
msgstr "Histórico dos objetos editados recentemente."
#: editor/inspector_dock.cpp
-#, fuzzy
msgid "Open documentation for this object."
-msgstr "Abrir Documentação"
+msgstr "Abrir documentação para esse objeto."
#: editor/inspector_dock.cpp editor/scene_tree_dock.cpp
msgid "Open Documentation"
@@ -4899,9 +4882,8 @@ msgid "Blend:"
msgstr "Misturar:"
#: editor/plugins/animation_blend_tree_editor_plugin.cpp
-#, fuzzy
msgid "Parameter Changed:"
-msgstr "Parâmetro Modificado"
+msgstr "Parâmetro Modificado:"
#: editor/plugins/animation_blend_tree_editor_plugin.cpp
#: editor/plugins/animation_tree_player_editor_plugin.cpp
@@ -6043,9 +6025,8 @@ msgid "Drag: Rotate selected node around pivot."
msgstr "Remover nó ou trilha selecionada."
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Alt+Drag: Move selected node."
-msgstr "Alt+Arrastar: Mover"
+msgstr "Alt+Arrastar: Mover nó selecionado."
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
@@ -6299,14 +6280,12 @@ msgid "Clear Pose"
msgstr "Limpar Pose"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Add Node Here"
-msgstr "Adicionar Nó"
+msgstr "Adicionar Nó Aqui"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Instance Scene Here"
-msgstr "Instanciar Cena(s)"
+msgstr "Instanciar Cena Aqui"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
@@ -6333,34 +6312,28 @@ msgid "Zoom to 12.5%"
msgstr "Zoom para 12.5%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 25%"
-msgstr "Reduzir"
+msgstr "Zoom de 25%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 50%"
-msgstr "Reduzir"
+msgstr "Zoom de 50%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 100%"
-msgstr "Reduzir"
+msgstr "Zoom de 100%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 200%"
-msgstr "Reduzir"
+msgstr "Zoom de 200%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 400%"
-msgstr "Reduzir"
+msgstr "Zoom de 400%"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Zoom to 800%"
-msgstr "Reduzir"
+msgstr "Zoom de 800%"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Zoom to 1600%"
@@ -7417,9 +7390,8 @@ msgid "Occluder Set Transform"
msgstr "Limpar Transformação"
#: editor/plugins/room_manager_editor_plugin.cpp
-#, fuzzy
msgid "Center Node"
-msgstr "Criar Nó"
+msgstr "Centralizar Nó"
#: editor/plugins/root_motion_editor_plugin.cpp
msgid "AnimationTree has no path set to an AnimationPlayer"
@@ -7555,12 +7527,10 @@ msgid "Move Down"
msgstr "Mover para Baixo"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Próximo Script"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Script anterior"
@@ -8054,15 +8024,13 @@ msgid "None"
msgstr "Nenhum"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Rotate"
-msgstr "Status:"
+msgstr "Rotacionar"
#. TRANSLATORS: This refers to the movement that changes the position of an object.
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Translate"
-msgstr "Translação:"
+msgstr "Translação"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale"
@@ -8102,38 +8070,32 @@ msgid "Size:"
msgstr "Tamanho:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Objects Drawn:"
-msgstr "Objetos Desenhados"
+msgstr "Objetos Desenhados:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Material Changes:"
-msgstr "Alterações de Material"
+msgstr "Alterações de Material:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Shader Changes:"
-msgstr "Alterações de Shader"
+msgstr "Alterações de Shader:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Surface Changes:"
-msgstr "Alterações de Superfície"
+msgstr "Alterações de Superfície:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Draw Calls:"
-msgstr "Chamadas de Desenho"
+msgstr "Chamadas de Desenho:"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Vertices:"
-msgstr "Vértices"
+msgstr "Vértices:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "FPS: %d (%s ms)"
-msgstr ""
+msgstr "FPS: %d (%s ms)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View."
@@ -13273,9 +13235,8 @@ msgid "Add Preload Node"
msgstr "Adicionar Nó de Pré-carregamento"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Add Node(s)"
-msgstr "Adicionar Nó"
+msgstr "Adicionar Nó(s)"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node(s) From Tree"
@@ -13542,17 +13503,15 @@ msgstr "Selecione um dispositivo da lista"
#: platform/android/export/export_plugin.cpp
msgid "Running on %s"
-msgstr ""
+msgstr "Executando em %s"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Exporting APK..."
-msgstr "Exportando tudo"
+msgstr "Exportando APK..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Uninstalling..."
-msgstr "Desinstalar"
+msgstr "Desinstalando..."
#: platform/android/export/export_plugin.cpp
#, fuzzy
@@ -13565,14 +13524,12 @@ msgid "Could not install to device: %s"
msgstr "Não foi possível instanciar cena!"
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Running on device..."
-msgstr "Rodando Script Personalizado..."
+msgstr "Executando no dispositivo..."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Could not execute on device."
-msgstr "Não foi possível criar a pasta."
+msgstr "Não foi possível executar no dispositivo."
#: platform/android/export/export_plugin.cpp
msgid "Unable to find the 'apksigner' tool."
@@ -13707,9 +13664,8 @@ msgid "'apksigner' returned with error #%d"
msgstr ""
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Verifying %s..."
-msgstr "Adicionando %s..."
+msgstr "Verificando %s..."
#: platform/android/export/export_plugin.cpp
msgid "'apksigner' verification of %s failed."
@@ -13797,9 +13753,8 @@ msgstr ""
"diretório do projeto gradle por saídas."
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Package not found: %s"
-msgstr "Animação não encontrada: '%s'"
+msgstr "Pacote não encontrado: '%s'"
#: platform/android/export/export_plugin.cpp
#, fuzzy
@@ -13823,9 +13778,8 @@ msgid ""
msgstr ""
#: platform/android/export/export_plugin.cpp
-#, fuzzy
msgid "Adding files..."
-msgstr "Adicionando %s..."
+msgstr "Adicionando arquivos..."
#: platform/android/export/export_plugin.cpp
#, fuzzy
diff --git a/editor/translations/ru.po b/editor/translations/ru.po
index 56788509e1..df2c3c1cdf 100644
--- a/editor/translations/ru.po
+++ b/editor/translations/ru.po
@@ -98,12 +98,13 @@
# enderlorde <madel.laboratories@gmail.com>, 2021.
# Олег Довгер <oleg.a.dovger@gmail.com>, 2021.
# Anna Malinovskaia <tacitcoast@gmail.com>, 2021.
+# mrvladus <mrvladus@yandex.ru>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-09-29 02:21+0000\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
"Last-Translator: Danil Alexeev <danil@alexeev.xyz>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/"
"godot/ru/>\n"
@@ -2497,9 +2498,8 @@ msgstr ""
"унаÑледованные) не могли быть удовлетворены."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Ðевозможно Ñохранить конвертированную текÑтуру:"
+msgstr "Ðе удалоÑÑŒ Ñохранить одну или неÑколько Ñцен!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2644,6 +2644,7 @@ msgstr "Сохранить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² «%s» перед закрытÐ
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
msgstr ""
+"%s больше не ÑущеÑтвует! ПожалуйÑта укажите новое меÑто Ð´Ð»Ñ ÑохранениÑ."
#: editor/editor_node.cpp
msgid ""
@@ -2785,7 +2786,7 @@ msgstr "Открыть закрытую Ñцену"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
msgstr ""
-"Ðе удаётÑÑ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ плагин: «%s». Ошибка ÑинтакÑичеÑкого разбора "
+"Ðе удаётÑÑ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒ плагин: «%s». Ошибка ÑинтакÑичеÑкого разбора файла "
"конфигурации."
#: editor/editor_node.cpp
@@ -4486,6 +4487,11 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"У Ð²Ð°Ñ ÐµÑть изменениÑ, которые ещё не были применены. Ðажмите "
+"«Переимпортировать», чтобы применить изменениÑ, внеÑённые в параметры "
+"импорта.\n"
+"ЕÑли выбрать другой реÑÑƒÑ€Ñ Ð² панели Â«Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема», не нажав Ñначала "
+"«Переимпортировать», то Ñделанные в панели «Импорт» Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð±ÑƒÐ´ÑƒÑ‚ потерÑны."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7453,12 +7459,10 @@ msgid "Move Down"
msgstr "ПеремеÑтить вниз"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "Следующий Ñкрипт"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Предыдущий Ñкрипт"
@@ -7877,9 +7881,8 @@ msgid "Left Orthogonal"
msgstr "Левый ортогональный"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "ПерÑпективный"
+msgstr "Левый перÑпективный"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8104,7 +8107,7 @@ msgstr "ПроÑлушиватель звука"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Enable Doppler"
-msgstr "Включить Ñффект Doppler"
+msgstr "Включить Ñффект Доплера"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Cinematic Preview"
@@ -8239,24 +8242,23 @@ msgstr "Вид Ñправа"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Вид Ñ Ð¾Ñ€Ð±Ð¸Ñ‚Ñ‹ вниз"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Вид Ñ Ð¾Ñ€Ð±Ð¸Ñ‚Ñ‹ влево"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Вид Ñ Ð¾Ñ€Ð±Ð¸Ñ‚Ñ‹ вправо"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "Вид Ñпереди"
+msgstr "Вид Ñ Ð¾Ñ€Ð±Ð¸Ñ‚Ñ‹ вверх"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Вид Ñ Ð¾Ñ€Ð±Ð¸Ñ‚Ñ‹ 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -12082,7 +12084,7 @@ msgstr "(ИÑточник)"
#: editor/scene_tree_editor.cpp
msgid "Node configuration warning:"
-msgstr "Конфигурации узла, предупреждение:"
+msgstr "Предупреждение о конфигурации узла:"
#: editor/scene_tree_editor.cpp
msgid ""
@@ -14565,6 +14567,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Очень низкое Ð²Ñ€ÐµÐ¼Ñ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ñ‚Ð°Ð¹Ð¼ÐµÑ€Ð° (< 0,05 Ñекунды) может привеÑти к "
+"значительно разному поведению, в завиÑимоÑти от чаÑтоты кадров рендеринга "
+"или физики.\n"
+"РаÑÑмотрите возможноÑть иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ†Ð¸ÐºÐ»Ð° обработки в Ñкрипте вмеÑто "
+"таймера Ñ Ð¾Ñ‡ÐµÐ½ÑŒ низким временем ожиданиÑ."
#: scene/main/viewport.cpp
msgid ""
@@ -14580,11 +14587,12 @@ msgstr ""
"либо узлу Ð´Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
-msgstr "Размер окна проÑмотра должен быть больше 0 Ð´Ð»Ñ Ñ€ÐµÐ½Ð´ÐµÑ€Ð¸Ð½Ð³Ð°."
+msgstr ""
+"Размер облаÑти проÑмотра должен быть больше или равен 2 пикÑелÑм в обоих "
+"измерениÑÑ…, чтобы отобразить что-либо."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/editor/translations/sv.po b/editor/translations/sv.po
index 2ae502bdd5..f22e9500e1 100644
--- a/editor/translations/sv.po
+++ b/editor/translations/sv.po
@@ -3,7 +3,7 @@
# Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).
# This file is distributed under the same license as the Godot source code.
# bergmarklund <davemcgroin@gmail.com>, 2017, 2018.
-# Christoffer Sundbom <christoffer_karlsson@live.se>, 2017.
+# Christoffer Sundbom <christoffer_karlsson@live.se>, 2017, 2021.
# Jakob Sinclair <sinclair.jakob@mailbox.org>, 2018.
# . <grenoscar@gmail.com>, 2018, 2020.
# Kristoffer Grundström <kristoffer.grundstrom1983@gmail.com>, 2018.
@@ -28,8 +28,8 @@ msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-08-10 21:40+0000\n"
-"Last-Translator: Kristoffer Grundström <swedishsailfishosuser@tutanota.com>\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
+"Last-Translator: Christoffer Sundbom <christoffer_karlsson@live.se>\n"
"Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/"
"godot/sv/>\n"
"Language: sv\n"
@@ -37,7 +37,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 4.8-dev\n"
+"X-Generator: Weblate 4.9-dev\n"
#: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp
#: modules/visual_script/visual_script_builtin_funcs.cpp
@@ -2146,9 +2146,8 @@ msgid "Methods"
msgstr "Metoder"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Theme Properties"
-msgstr "Egenskaper"
+msgstr "Tema Egenskaper"
#: editor/editor_help.cpp
msgid "Enumerations"
diff --git a/editor/translations/uk.po b/editor/translations/uk.po
index d662cfea7b..db324bbc2c 100644
--- a/editor/translations/uk.po
+++ b/editor/translations/uk.po
@@ -22,7 +22,7 @@ msgstr ""
"Project-Id-Version: Ukrainian (Godot Engine)\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2021-09-22 20:30+0000\n"
+"PO-Revision-Date: 2021-10-18 15:35+0000\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/"
"godot/uk/>\n"
@@ -2426,9 +2426,8 @@ msgstr ""
"уÑпадковані) не задоволені."
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити підпроцеÑ!"
+msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ одну або декілька Ñцен!"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2571,7 +2570,7 @@ msgstr "Зберегти зміни, внеÑені до '%s' перед закÑ
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "%s вже не Ñ–Ñнує! Будь лаÑка, вкажіть нове міÑце Ð´Ð»Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ."
#: editor/editor_node.cpp
msgid ""
@@ -4421,6 +4420,11 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"ВнеÑені вами зміни ще не заÑтоÑовано. ÐатиÑніть кнопку «Імпортувати "
+"повторно», щоб заÑтоÑувати зміни у параметрах імпортуваннÑ.\n"
+"Вибір іншого реÑурÑу на бічній панелі файлової ÑиÑтеми без натиÑÐºÐ°Ð½Ð½Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ "
+"«Імпортувати повторно» призведе до Ð²Ñ–Ð´ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð·Ð¼Ñ–Ð½, Ñкі було внеÑено на "
+"бічній панелі імпортуваннÑ."
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7400,12 +7404,10 @@ msgid "Move Down"
msgstr "ПереміÑтити вниз"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "ÐаÑтупний Ñкрипт"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "Попередній Ñкрипт"
@@ -7824,9 +7826,8 @@ msgid "Left Orthogonal"
msgstr "Лівий ортогональний"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "ПерÑпектива"
+msgstr "Ліва перÑпектива"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8185,24 +8186,23 @@ msgstr "ВиглÑд Ñправа"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "Орбітальний вид вниз"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "Орбітальний вид ліворуч"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "Орбітальний вид праворуч"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "ВиглÑд Ñпереду"
+msgstr "Орбітальний вид згори"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "Орбітальний вид 180"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -14554,6 +14554,11 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"Дуже малі проміжки чаÑу Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° таймером (< 0.05 Ñекунд) можуть "
+"призводити до Ñуттєво різних результатів Ð´Ð»Ñ Ñ€Ñ–Ð·Ð½Ð¸Ñ… оброблених або фізичних "
+"чаÑтот кадрів.\n"
+"Вам варто ÑкориÑтатиÑÑ Ñ†Ð¸ÐºÐ»Ð¾Ð¼ процеÑу Ñкрипту заміÑть Timer Ð´Ð»Ñ Ð´ÑƒÐ¶Ðµ малих "
+"проміжків очікуваннÑ."
#: scene/main/viewport.cpp
msgid ""
@@ -14568,13 +14573,12 @@ msgstr ""
"пов'Ñзати Ñ—Ñ— внутрішню текÑтуру з одним із вузлів Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ."
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
msgstr ""
"Щоб програма могла хоч щоÑÑŒ показати, розмір Ð¿Ð¾Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду має бути більшим "
-"за 0."
+"або рівним 2 пікÑелÑм в обох вимірах."
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po
index f17a8af827..82159d534e 100644
--- a/editor/translations/zh_CN.po
+++ b/editor/translations/zh_CN.po
@@ -79,12 +79,15 @@
# suplife <2634557184@qq.com>, 2021.
# luoji <564144019@qq.com>, 2021.
# zeng haochen <m18621006730@163.com>, 2021.
+# Sam Sun <oppositenormal@outlook.com>, 2021.
+# è‹è½¼ <youwanyuyu@gmail.com>, 2021.
+# nitenook <admin@alterbaum.net>, 2021.
msgid ""
msgstr ""
"Project-Id-Version: Chinese (Simplified) (Godot Engine)\n"
"Report-Msgid-Bugs-To: https://github.com/godotengine/godot\n"
"POT-Creation-Date: 2018-01-20 12:15+0200\n"
-"PO-Revision-Date: 2021-10-08 03:03+0000\n"
+"PO-Revision-Date: 2021-10-21 10:31+0000\n"
"Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n"
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
"godot-engine/godot/zh_Hans/>\n"
@@ -2435,9 +2438,8 @@ msgid ""
msgstr "无法ä¿å­˜åœºæ™¯ã€‚å¯èƒ½æ˜¯å› ä¸ºä¾èµ–项(实例或继承)无法满足。"
#: editor/editor_node.cpp
-#, fuzzy
msgid "Could not save one or more scenes!"
-msgstr "无法ä¿å­˜è½¬æ¢çš„贴图:"
+msgstr "无法ä¿å­˜ä¸€ä¸ªæˆ–多个场景ï¼"
#: editor/editor_node.cpp
msgid "Save All Scenes"
@@ -2573,7 +2575,7 @@ msgstr "是å¦åœ¨å…³é—­å‰ä¿å­˜å¯¹ “%s†的更改?"
#: editor/editor_node.cpp
msgid "%s no longer exists! Please specify a new save location."
-msgstr ""
+msgstr "路径 %s å·²ä¸å­˜åœ¨ï¼è¯·é‡æ–°é€‰æ‹©æ–°çš„ä¿å­˜è·¯å¾„。"
#: editor/editor_node.cpp
msgid ""
@@ -4362,6 +4364,8 @@ msgid ""
"Selecting another resource in the FileSystem dock without clicking Reimport "
"first will discard changes made in the Import dock."
msgstr ""
+"æœ‰äº›ä¿®æ”¹æœªè¢«åº”ç”¨ã€‚ç‚¹å‡»â€œé‡æ–°å¯¼å…¥â€æ¥åº”用导入的修改。\n"
+"若在选择å¦ä¸€ä¸ªèµ„æºå‰æ²¡æœ‰ç‚¹å‡»â€œé‡æ–°å¯¼å…¥â€ï¼Œåˆ™ä¼šå¿½ç•¥å¯¼å…¥çš„修改。"
#: editor/import_dock.cpp
msgid "Import As:"
@@ -7289,12 +7293,10 @@ msgid "Move Down"
msgstr "å‘下移动"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Next Script"
msgstr "下一个脚本"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Previous Script"
msgstr "上一个脚本"
@@ -7709,9 +7711,8 @@ msgid "Left Orthogonal"
msgstr "正交左视图"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Left Perspective"
-msgstr "é€è§†"
+msgstr "é€è§†å·¦è§†å›¾"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right Orthogonal"
@@ -8067,24 +8068,23 @@ msgstr "å³è§†å›¾"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Down"
-msgstr ""
+msgstr "视图å‘下环绕"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Left"
-msgstr ""
+msgstr "视图å‘左环绕"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View Right"
-msgstr ""
+msgstr "视图å‘å³çŽ¯ç»•"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Orbit View Up"
-msgstr "å‰è§†å›¾"
+msgstr "视图å‘上环绕"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orbit View 180"
-msgstr ""
+msgstr "视图环绕至背é¢"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal View"
@@ -14195,6 +14195,9 @@ msgid ""
"Consider using a script's process loop instead of relying on a Timer for "
"very low wait times."
msgstr ""
+"计时器等待时间éžå¸¸çŸ­ï¼ˆå°äºŽ 0.05 秒)时å¯èƒ½ä¼šæ ¹æ®æ¸²æŸ“帧率和物ç†å¸§çŽ‡çš„ä¸åŒè€Œè¡¨"
+"现得æžä¸ºä¸åŒã€‚\n"
+"建议使用脚本的 process 循环代替此类计时器。"
#: scene/main/viewport.cpp
msgid ""
@@ -14203,16 +14206,15 @@ msgid ""
"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
"texture to some node for display."
msgstr ""
-"这个 Viewport 未被设置为渲染目标。如果你想让其直接在å±å¹•上显示内容,请使其æˆ"
-"为 Control çš„å­èŠ‚ç‚¹ï¼Œè¿™æ ·ä¸€æ¥è¯¥ Viewport æ‰ä¼šæœ‰å¤§å°ã€‚å¦åˆ™è¯·ä¸ºå…¶è®¾ç½® "
+"这个视窗未被设置为渲染目标。如果你想让其直接在å±å¹•上显示内容,请使其æˆä¸º "
+"Control çš„å­èŠ‚ç‚¹ï¼Œè¿™æ ·ä¸€æ¥è¯¥ Viewport æ‰ä¼šæœ‰å¤§å°ã€‚å¦åˆ™è¯·ä¸ºå…¶è®¾ç½® "
"RenderTarget 并分é…å…¶å†…éƒ¨çº¹ç†æ¥æ˜¾ç¤ºã€‚"
#: scene/main/viewport.cpp
-#, fuzzy
msgid ""
"The Viewport size must be greater than or equal to 2 pixels on both "
"dimensions to render anything."
-msgstr "Viewport 大å°å¤§äºŽ 0 æ—¶æ‰èƒ½è¿›è¡Œæ¸²æŸ“。"
+msgstr "Viewport 的宽高都大于等于 2 åƒç´ æ—¶æ‰èƒ½è¿›è¡Œæ¸²æŸ“。"
#: scene/resources/occluder_shape.cpp
msgid "OccluderShapeSphere Set Spheres"
diff --git a/main/main.cpp b/main/main.cpp
index 2a9446d4ed..d4a6216e35 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -918,6 +918,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
cmdline_tool = true;
dump_extension_api = true;
print_line("Dumping Extension API");
+ // Hack. Not needed but otherwise we end up detecting that this should
+ // run the project instead of a cmdline tool.
+ // Needs full refactoring to fix properly.
+ main_args.push_back(I->get());
} else if (I->get() == "--export" || I->get() == "--export-debug" ||
I->get() == "--export-pack") { // Export project
// Actually handling is done in start().
@@ -2068,6 +2072,8 @@ bool Main::start() {
if (check_only) {
if (!script_res->is_valid()) {
OS::get_singleton()->set_exit_code(EXIT_FAILURE);
+ } else {
+ OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
}
return false;
}
diff --git a/methods.py b/methods.py
index 77a1085baf..b2eb7b7c77 100644
--- a/methods.py
+++ b/methods.py
@@ -1,9 +1,9 @@
-import collections
import os
import re
import glob
import subprocess
from collections import OrderedDict
+from collections.abc import Mapping
from typing import Iterator
# We need to define our own `Action` method to control the verbosity of output
@@ -662,7 +662,7 @@ def generate_vs_project(env, num_jobs):
batch_file = find_visual_c_batch_file(env)
if batch_file:
- class ModuleConfigs(collections.Mapping):
+ class ModuleConfigs(Mapping):
# This version information (Win32, x64, Debug, Release, Release_Debug seems to be
# required for Visual Studio to understand that it needs to generate an NMAKE
# project. Do not modify without knowing what you are doing.
diff --git a/misc/dist/osx/editor.entitlements b/misc/dist/osx/editor.entitlements
index 5496f65dcc..d0137910a3 100644
--- a/misc/dist/osx/editor.entitlements
+++ b/misc/dist/osx/editor.entitlements
@@ -2,11 +2,19 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
- <key>com.apple.security.device.audio-input</key>
+ <key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
- <key>com.apple.security.device.camera</key>
+ <key>com.apple.security.cs.allow-jit</key>
+ <true/>
+ <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
+ <true/>
+ <key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
+ <key>com.apple.security.device.audio-input</key>
+ <true/>
+ <key>com.apple.security.device.camera</key>
+ <true/>
</dict>
</plist>
diff --git a/misc/dist/osx/editor_mono.entitlements b/misc/dist/osx/editor_mono.entitlements
deleted file mode 100644
index c61c287652..0000000000
--- a/misc/dist/osx/editor_mono.entitlements
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.security.cs.allow-dyld-environment-variables</key>
- <true/>
- <key>com.apple.security.cs.allow-jit</key>
- <true/>
- <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
- <true/>
- <key>com.apple.security.cs.disable-library-validation</key>
- <true/>
- <key>com.apple.security.device.audio-input</key>
- <true/>
- <key>com.apple.security.device.camera</key>
- <true/>
-</dict>
-</plist>
diff --git a/misc/hooks/README.md b/misc/hooks/README.md
index 5661c239ed..e420c6cb5c 100644
--- a/misc/hooks/README.md
+++ b/misc/hooks/README.md
@@ -12,8 +12,8 @@ contributors to make sure they comply with our requirements.
`PATH`, or if you want to enable colored output with `pygmentize`.
- Pre-commit hook for `black`: Applies `black` to the staged Python files
before accepting a commit.
-- Pre-commit hook for `makerst`: Checks the class reference syntax using
- `makerst.py`.
+- Pre-commit hook for `make_rst`: Checks the class reference syntax using
+ `make_rst.py`.
## Installation
diff --git a/misc/hooks/pre-commit b/misc/hooks/pre-commit
index 40cb00253b..ab0fc8176f 100755
--- a/misc/hooks/pre-commit
+++ b/misc/hooks/pre-commit
@@ -14,7 +14,7 @@
# as this script. Hooks should return 0 if successful and nonzero to cancel the
# commit. They are executed in the order in which they are listed.
#HOOKS="pre-commit-compile pre-commit-uncrustify"
-HOOKS="pre-commit-clang-format pre-commit-black pre-commit-makerst"
+HOOKS="pre-commit-clang-format pre-commit-black pre-commit-make-rst"
###########################################################
# There should be no need to change anything below this line.
diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format
index 81bc412944..de5d9c3f06 100755
--- a/misc/hooks/pre-commit-clang-format
+++ b/misc/hooks/pre-commit-clang-format
@@ -76,11 +76,11 @@ fi
# To get consistent formatting, we recommend contributors to use the same
# clang-format version as CI.
-RECOMMENDED_CLANG_FORMAT_MAJOR_MIN="11"
-RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="12"
+RECOMMENDED_CLANG_FORMAT_MAJOR_MIN="12"
+RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="13"
if [ ! -x "$CLANG_FORMAT" ] ; then
- message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR.x.x."
+ message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX."
if [ ! -t 1 ] ; then
if [ -x "$ZENITY" ] ; then
@@ -108,7 +108,7 @@ CLANG_FORMAT_VERSION="$(clang-format --version | sed "s/[^0-9\.]*\([0-9\.]*\).*/
CLANG_FORMAT_MAJOR="$(echo "$CLANG_FORMAT_VERSION" | cut -d. -f1)"
if [[ "$CLANG_FORMAT_MAJOR" -lt "$RECOMMENDED_CLANG_FORMAT_MAJOR_MIN" || "$CLANG_FORMAT_MAJOR" -gt "$RECOMMENDED_CLANG_FORMAT_MAJOR_MAX" ]]; then
- echo "Warning: Your clang-format binary is the wrong version ($CLANG_FORMAT_VERSION, expected between $RECOMMENDED_CLANG_FORMAT_MAJOR_MIN.x.x and $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX.x.x)."
+ echo "Warning: Your clang-format binary is the wrong version ($CLANG_FORMAT_VERSION, expected between $RECOMMENDED_CLANG_FORMAT_MAJOR_MIN and $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX)."
echo " Consider upgrading or downgrading clang-format as formatting may not be applied correctly."
fi
@@ -146,7 +146,7 @@ do
# +++ - timestamp
# to both lines working on the same file and having a/ and b/ prefix.
# Else it can not be applied with 'git apply'.
- "$CLANG_FORMAT" -style=file "$file" | \
+ "$CLANG_FORMAT" -style=file "$file" --Wno-error=unknown | \
diff -u "$file" - | \
sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch"
done
diff --git a/misc/hooks/pre-commit-makerst b/misc/hooks/pre-commit-make-rst
index b990788b99..38b397c494 100755
--- a/misc/hooks/pre-commit-makerst
+++ b/misc/hooks/pre-commit-make-rst
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
-# Git pre-commit hook that checks the class reference syntax using makerst.py.
+# Git pre-commit hook that checks the class reference syntax using make_rst.py.
# Workaround because we can't execute the .py file directly on windows
PYTHON=python
@@ -9,4 +9,4 @@ if [[ "$py_ver" != "3" ]]; then
PYTHON+=3
fi
-$PYTHON doc/tools/makerst.py doc/classes modules --dry-run
+$PYTHON doc/tools/make_rst.py doc/classes modules --dry-run
diff --git a/misc/scripts/clang_format.sh b/misc/scripts/clang_format.sh
index bcd63aa73b..b0020da597 100755
--- a/misc/scripts/clang_format.sh
+++ b/misc/scripts/clang_format.sh
@@ -23,7 +23,7 @@ while IFS= read -rd '' f; do
for extension in ${CLANG_FORMAT_FILE_EXTS[@]}; do
if [[ "$f" == *"$extension" ]]; then
# Run clang-format.
- clang-format -i "$f"
+ clang-format --Wno-error=unknown -i "$f"
# Fix copyright headers, but not all files get them.
if [[ "$f" == *"inc" ]]; then
continue 2
diff --git a/misc/scripts/compare_extension_api.py b/misc/scripts/compare_extension_api.py
new file mode 100755
index 0000000000..f96db4278c
--- /dev/null
+++ b/misc/scripts/compare_extension_api.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+# TODO:
+# Add a process that compares the original godot-cpp/godot-headers/extension_api.json with the new extension_api.json (both passed as arguments) and reports any API calls that have been removed.
+# If we only have additions or no changes to the file, we pass
+# For now we deem this too early because the API isn't stable enough yet.
+
+sys.exit(0)
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 7cf666c119..c8b516c951 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -145,7 +145,6 @@ public:
virtual void dispatch_callbacks();
void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
- void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
void clear_overlaps(bool p_notify);
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 81b832fb42..3a2370ff31 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -116,6 +116,7 @@ void SoftBodyBullet::set_soft_mesh(RID p_mesh) {
}
Array arrays = RenderingServer::get_singleton()->mesh_surface_get_arrays(soft_mesh, 0);
+ ERR_FAIL_COND(arrays.is_empty());
bool success = set_trimesh_body_shape(arrays[RS::ARRAY_INDEX], arrays[RS::ARRAY_VERTEX]);
if (!success) {
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 14e7896295..863936ab8c 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -576,6 +576,7 @@ void CSGShape3D::_validate_property(PropertyInfo &property) const {
} else if (is_collision_prefixed && !bool(get("use_collision"))) {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
+ GeometryInstance3D::_validate_property(property);
}
Array CSGShape3D::get_meshes() const {
diff --git a/modules/denoise/config.py b/modules/denoise/config.py
index 49a1f036ed..6f650354cb 100644
--- a/modules/denoise/config.py
+++ b/modules/denoise/config.py
@@ -1,11 +1,12 @@
def can_build(env, platform):
# Thirdparty dependency OpenImage Denoise includes oneDNN library
- # which only supports 64-bit architectures.
+ # and the version we use only supports x86_64.
# It's also only relevant for tools build and desktop platforms,
# as doing lightmap generation and denoising on Android or HTML5
# would be a bit far-fetched.
desktop_platforms = ["linuxbsd", "osx", "windows"]
- return env["tools"] and platform in desktop_platforms and env["bits"] == "64" and env["arch"] != "arm64"
+ supported_arch = env["bits"] == "64" and env["arch"] != "arm64" and not env["arch"].startswith("rv")
+ return env["tools"] and platform in desktop_platforms and supported_arch
def configure(env):
diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp
index 66b0153308..bb95d120af 100644
--- a/modules/fbx/tools/import_utils.cpp
+++ b/modules/fbx/tools/import_utils.cpp
@@ -45,27 +45,27 @@ Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
- ret.set_euler_zyx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
break;
case FBXDocParser::Model::RotOrder_EulerXZY:
- ret.set_euler_yzx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
break;
case FBXDocParser::Model::RotOrder_EulerYZX:
- ret.set_euler_xzy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
break;
case FBXDocParser::Model::RotOrder_EulerYXZ:
- ret.set_euler_zxy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
break;
case FBXDocParser::Model::RotOrder_EulerZXY:
- ret.set_euler_yxz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
break;
case FBXDocParser::Model::RotOrder_EulerZYX:
- ret.set_euler_xyz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
break;
case FBXDocParser::Model::RotOrder_SphericXYZ:
@@ -89,22 +89,22 @@ Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basi
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
- return p_rotation.get_euler_zyx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
case FBXDocParser::Model::RotOrder_EulerXZY:
- return p_rotation.get_euler_yzx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
case FBXDocParser::Model::RotOrder_EulerYZX:
- return p_rotation.get_euler_xzy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
case FBXDocParser::Model::RotOrder_EulerYXZ:
- return p_rotation.get_euler_zxy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
case FBXDocParser::Model::RotOrder_EulerZXY:
- return p_rotation.get_euler_yxz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
case FBXDocParser::Model::RotOrder_EulerZYX:
- return p_rotation.get_euler_xyz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
case FBXDocParser::Model::RotOrder_SphericXYZ:
// TODO
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index fb46bafb3c..368eb67fa6 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -1139,15 +1139,12 @@ void NativeScriptLanguage::profiling_start() {
MutexLock lock(mutex);
profile_data.clear();
- profiling = true;
#endif
}
void NativeScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
MutexLock lock(mutex);
-
- profiling = false;
#endif
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index a7647e8c59..2364c6c0f6 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -269,7 +269,6 @@ private:
};
Map<StringName, ProfileData> profile_data;
- bool profiling = false;
public:
// These two maps must only be touched on the main thread
@@ -295,8 +294,6 @@ public:
return singleton;
}
- void _hacky_api_anchor();
-
_FORCE_INLINE_ void set_language_index(int p_idx) { lang_idx = p_idx; }
#ifndef NO_THREADS
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index bdae265db2..81e711bafc 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -69,8 +69,6 @@ public:
virtual ScriptLanguage *get_language();
- void set_path(const String &p_path);
-
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual void refcount_incremented();
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 85a44a8cc1..f1413f0133 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -50,10 +50,8 @@ class GDScriptLanguageServer : public EditorPlugin {
private:
void _notification(int p_what);
- void _iteration();
public:
- Error parse_script_file(const String &p_path);
GDScriptLanguageServer();
void start();
void stop();
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
index de269b92b3..fead2df854 100644
--- a/modules/gdscript/tests/scripts/runtime/features/stringify.gd
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
@@ -17,7 +17,7 @@ func test():
print(Plane(1, 2, 3, 4))
print(Quaternion(1, 2, 3, 4))
print(AABB(Vector3.ZERO, Vector3.ONE))
- print(Basis(Vector3(0, 0, 0)))
+ print(Basis.from_euler(Vector3(0, 0, 0)))
print(Transform3D.IDENTITY)
print(Color(1, 2, 3, 4))
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index b8da3a713b..3a8cab7e7e 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -120,26 +120,26 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
return Error::FAILED;
}
- /* STEP 7 SERIALIZE IMAGES */
- err = _serialize_images(state, p_path);
+ /* STEP 7 SERIALIZE ANIMATIONS */
+ err = _serialize_animations(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 8 SERIALIZE TEXTURES */
- err = _serialize_textures(state);
+ /* STEP 8 SERIALIZE ACCESSORS */
+ err = _encode_accessors(state);
if (err != OK) {
return Error::FAILED;
}
- // /* STEP 9 SERIALIZE ANIMATIONS */
- err = _serialize_animations(state);
+ /* STEP 9 SERIALIZE IMAGES */
+ err = _serialize_images(state, p_path);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 10 SERIALIZE ACCESSORS */
- err = _encode_accessors(state);
+ /* STEP 10 SERIALIZE TEXTURES */
+ err = _serialize_textures(state);
if (err != OK) {
return Error::FAILED;
}
@@ -4369,6 +4369,10 @@ Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) {
json_skin["name"] = gltf_skin->get_name();
json_skins.push_back(json_skin);
}
+ if (!state->skins.size()) {
+ return OK;
+ }
+
state->json["skins"] = json_skins;
return OK;
}
@@ -6460,7 +6464,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
String shape_name = mesh->get_blend_shape_name(shape_i);
NodePath shape_path = String(path) + ":" + shape_name;
- int32_t shape_track_i = animation->find_track(shape_path);
+ int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE);
if (shape_track_i == -1) {
GLTFAnimation::Channel<float> weight;
weight.interpolation = GLTFAnimation::INTERP_LINEAR;
@@ -6756,33 +6760,36 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
const uint32_t magic = 0x46546C67; // GLTF
const int32_t header_size = 12;
const int32_t chunk_header_size = 8;
-
- for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) {
- json += " ";
- }
CharString cs = json.utf8();
- const uint32_t text_chunk_length = cs.length();
-
+ const uint32_t text_data_length = cs.length();
+ const uint32_t text_chunk_length = ((text_data_length + 3) & (~3));
const uint32_t text_chunk_type = 0x4E4F534A; //JSON
- int32_t binary_data_length = 0;
+
+ uint32_t binary_data_length = 0;
if (state->buffers.size()) {
binary_data_length = state->buffers[0].size();
}
- const int32_t binary_chunk_length = binary_data_length;
- const int32_t binary_chunk_type = 0x004E4942; //BIN
+ const uint32_t binary_chunk_length = ((binary_data_length + 3) & (~3));
+ const uint32_t binary_chunk_type = 0x004E4942; //BIN
f->create(FileAccess::ACCESS_RESOURCES);
f->store_32(magic);
f->store_32(state->major_version); // version
- f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length
+ f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length
f->store_32(text_chunk_length);
f->store_32(text_chunk_type);
f->store_buffer((uint8_t *)&cs[0], cs.length());
+ for (uint32_t pad_i = text_data_length; pad_i < text_chunk_length; pad_i++) {
+ f->store_8(' ');
+ }
if (binary_chunk_length) {
f->store_32(binary_chunk_length);
f->store_32(binary_chunk_type);
f->store_buffer(state->buffers[0].ptr(), binary_data_length);
}
+ for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) {
+ f->store_8(0);
+ }
f->close();
} else {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index a1d82a4649..f2f0b439a5 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -269,8 +269,6 @@ private:
Error _reparent_non_joint_skeleton_subtrees(
Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
const Vector<GLTFNodeIndex> &non_joints);
- Error _reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
- const GLTFNodeIndex node_index);
Error _determine_skeleton_roots(Ref<GLTFState> state,
const GLTFSkeletonIndex skel_i);
Error _create_skeletons(Ref<GLTFState> state);
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h
index 6554c74a21..92e6ab88c4 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.h
+++ b/modules/mbedtls/packet_peer_mbed_dtls.h
@@ -59,8 +59,6 @@ protected:
Ref<SSLContextMbedTLS> ssl_ctx;
mbedtls_timing_delay_context timer;
- static void _bind_methods();
-
Error _do_handshake();
int _set_cookie();
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h
index 1b55a54a10..5692dec1b6 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/ssl_context_mbedtls.h
@@ -67,8 +67,6 @@ class SSLContextMbedTLS : public RefCounted {
protected:
bool inited = false;
- static PackedByteArray _read_file(String p_path);
-
public:
static void print_mbedtls_error(int p_ret);
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index b89d7fb238..407479e3cc 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -50,8 +50,6 @@ private:
protected:
Ref<SSLContextMbedTLS> ssl_ctx;
- static void _bind_methods();
-
Error _do_handshake();
public:
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 4c851a2989..df02d9a309 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -2,7 +2,7 @@ supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku
def can_build(env, platform):
- return True
+ return not env["arch"].startswith("rv")
def configure(env):
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 26a04a358d..531f600c3f 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1813,8 +1813,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
- p_properties->push_back(E.value);
+ for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = script->member_info.front(); E; E = E.next()) {
+ p_properties->push_front(E.value());
}
// Call _get_property_list
@@ -1839,10 +1839,9 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
for (int i = 0, size = array.size(); i < size; i++) {
p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
}
- return;
}
- break;
+ return;
}
top = top->get_parent_class();
@@ -1865,8 +1864,9 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
}
void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
- if (!script->is_valid() || !script->script_class)
+ if (!script->is_valid() || !script->script_class) {
return;
+ }
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -3499,9 +3499,9 @@ Ref<Script> CSharpScript::get_base_script() const {
return Ref<Script>();
}
-void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const {
- for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
- p_list->push_back(E.value);
+void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = member_info.front(); E; E = E.next()) {
+ r_list->push_front(E.value());
}
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index afc17f694a..c998d9c1e4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -154,7 +154,7 @@ private:
Set<StringName> exported_members_names;
#endif
- Map<StringName, PropertyInfo> member_info;
+ OrderedHashMap<StringName, PropertyInfo> member_info;
void _clear();
@@ -215,7 +215,7 @@ public:
void get_script_signal_list(List<MethodInfo> *r_signals) const override;
bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- void get_script_property_list(List<PropertyInfo> *p_list) const override;
+ void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
void get_members(Set<StringName> *p_members) override;
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index 1d7bfaf0a4..bc09e1ebf9 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -121,15 +121,20 @@ namespace GodotTools.IdeMessaging
this.messageHandler = messageHandler;
this.logger = logger;
- // TODO: Need to fetch the project data dir name from ProjectSettings instead of defaulting to ".godot"
string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
+ // FileSystemWatcher requires an existing directory
+ if (!Directory.Exists(projectMetadataDir)) {
+ // Check if the non hidden version exists
+ string nonHiddenProjectMetadataDir = Path.Combine(godotProjectDir, "godot", "mono", "metadata");
+ if (Directory.Exists(nonHiddenProjectMetadataDir)) {
+ projectMetadataDir = nonHiddenProjectMetadataDir;
+ } else {
+ Directory.CreateDirectory(projectMetadataDir);
+ }
+ }
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
- // FileSystemWatcher requires an existing directory
- if (!Directory.Exists(projectMetadataDir))
- Directory.CreateDirectory(projectMetadataDir);
-
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
}
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index bb6bc578a4..8fd3a13e1f 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -489,7 +489,7 @@ NavigationMeshGenerator::~NavigationMeshGenerator() {
}
void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
- ERR_FAIL_COND(!p_nav_mesh.is_valid());
+ ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid navigation mesh.");
#ifdef TOOLS_ENABLED
EditorProgress *ep(nullptr);
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
index 5de36c5322..2f8bacd4ae 100644
--- a/modules/raycast/config.py
+++ b/modules/raycast/config.py
@@ -1,5 +1,7 @@
def can_build(env, platform):
# Depends on Embree library, which only supports x86_64 and aarch64.
+ if env["arch"].startswith("rv"):
+ return False
if platform == "android":
return env["android_arch"] in ["arm64v8", "x86_64"]
diff --git a/modules/regex/config.py b/modules/regex/config.py
index df9f44cb95..1248a8374d 100644
--- a/modules/regex/config.py
+++ b/modules/regex/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["arch"].startswith("rv")
def configure(env):
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index e95369ead7..c459141265 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -1211,6 +1211,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
}
_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 193588c4ed..f598eb80ea 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -670,6 +670,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
}
_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
diff --git a/modules/theora/config.py b/modules/theora/config.py
index b063ed51f9..7f354a8fda 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,4 +1,6 @@
def can_build(env, platform):
+ if env["arch"].startswith("rv"):
+ return False
return env.module_check_dependencies("theora", ["ogg", "vorbis"])
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index bf2d8e9683..78881f0a53 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -90,12 +90,6 @@ public:
void set_stack_size(int p_size);
int get_stack_size() const;
- void set_return_type_enabled(bool p_returns);
- bool is_return_type_enabled() const;
-
- void set_return_type(Variant::Type p_type);
- Variant::Type get_return_type() const;
-
void set_rpc_mode(Multiplayer::RPCMode p_mode);
Multiplayer::RPCMode get_rpc_mode() const;
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
index 93c8bd9604..508b5a12a1 100644
--- a/modules/websocket/wsl_server.h
+++ b/modules/websocket/wsl_server.h
@@ -53,7 +53,7 @@ private:
Ref<StreamPeer> connection;
bool use_ssl = false;
- int time = 0;
+ uint64_t time = 0;
uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {};
int req_pos = 0;
String key;
diff --git a/platform/android/audio_driver_opensl.h b/platform/android/audio_driver_opensl.h
index e3efaddba2..fcc2513f3f 100644
--- a/platform/android/audio_driver_opensl.h
+++ b/platform/android/audio_driver_opensl.h
@@ -59,7 +59,6 @@ class AudioDriverOpenSL : public AudioDriver {
SLObjectItf sl;
SLEngineItf EngineItf;
SLObjectItf OutputMix;
- SLVolumeItf volumeItf;
SLObjectItf player;
SLObjectItf recorder;
SLAndroidSimpleBufferQueueItf bufferQueueItf;
@@ -68,7 +67,6 @@ class AudioDriverOpenSL : public AudioDriver {
SLDataFormat_PCM pcm;
SLDataSink audioSink;
SLDataLocator_OutputMix locator_outputmix;
- SLBufferQueueState state;
static AudioDriverOpenSL *s_ad;
@@ -89,8 +87,6 @@ class AudioDriverOpenSL : public AudioDriver {
virtual Error capture_init_device();
public:
- void set_singleton();
-
virtual const char *get_name() const;
virtual Error init();
diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp
index 851bd0ac52..311614234a 100644
--- a/platform/android/export/gradle_export_util.cpp
+++ b/platform/android/export/gradle_export_util.cpp
@@ -128,11 +128,26 @@ Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_
return err;
}
+String _android_xml_escape(const String &p_string) {
+ // Android XML requires strings to be both valid XML (`xml_escape()`) but also
+ // to escape characters which are valid XML but have special meaning in Android XML.
+ // https://developer.android.com/guide/topics/resources/string-resource.html#FormattingAndStyling
+ // Note: Didn't handle U+XXXX unicode chars, could be done if needed.
+ return p_string
+ .replace("@", "\\@")
+ .replace("?", "\\?")
+ .replace("'", "\\'")
+ .replace("\"", "\\\"")
+ .replace("\n", "\\n")
+ .replace("\t", "\\t")
+ .xml_escape(false);
+}
+
// Creates strings.xml files inside the gradle project for different locales.
Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) {
print_verbose("Creating strings resources for supported locales for project " + project_name);
// Stores the string into the default values directory.
- String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true));
+ String processed_default_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(project_name));
store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string);
// Searches the Gradle project res/ directory to find all supported locales
@@ -158,7 +173,7 @@ Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset
String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml";
if (ProjectSettings::get_singleton()->has_setting(property_name)) {
String locale_project_name = ProjectSettings::get_singleton()->get(property_name);
- String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true));
+ String processed_xml_string = vformat(godot_project_name_xml_string, _android_xml_escape(locale_project_name));
print_verbose("Storing project name for locale " + locale + " under " + locale_directory);
store_string_at_path(locale_directory, processed_xml_string);
} else {
diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle
index 87bb2ea218..efdcc6c77b 100644
--- a/platform/android/java/build.gradle
+++ b/platform/android/java/build.gradle
@@ -158,9 +158,9 @@ def templateBuildTasks() {
/**
* Master task used to coordinate the tasks defined above to generate the set of Godot templates.
*/
-task generateGodotTemplates(type: GradleBuild) {
- startParameter.excludedTaskNames = templateExcludedBuildTask()
- tasks = templateBuildTasks()
+task generateGodotTemplates {
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+ dependsOn = templateBuildTasks()
finalizedBy 'zipCustomBuild'
}
@@ -168,12 +168,12 @@ task generateGodotTemplates(type: GradleBuild) {
/**
* Generates the same output as generateGodotTemplates but with dev symbols
*/
-task generateDevTemplate (type: GradleBuild) {
+task generateDevTemplate {
// add parameter to set symbols to true
- startParameter.projectProperties += [doNotStrip: true]
+ gradle.startParameter.projectProperties += [doNotStrip: true]
- startParameter.excludedTaskNames = templateExcludedBuildTask()
- tasks = templateBuildTasks()
+ gradle.startParameter.excludedTaskNames += templateExcludedBuildTask()
+ dependsOn = templateBuildTasks()
finalizedBy 'zipCustomBuild'
}
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 034885aa32..94ca51f75a 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -263,7 +263,6 @@ Size2i OS_Android::get_display_size() const {
void OS_Android::set_context_is_16_bits(bool p_is_16) {
#if defined(OPENGL_ENABLED)
- //use_16bits_fbo = p_is_16;
//if (rasterizer)
// rasterizer->set_force_16_bits_fbo(p_is_16);
#endif
@@ -325,7 +324,6 @@ OS_Android::OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_god
#if defined(OPENGL_ENABLED)
gl_extensions = nullptr;
use_gl2 = false;
- use_16bits_fbo = false;
#endif
#if defined(VULKAN_ENABLED)
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index ce8083388f..9bb37325cf 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -48,7 +48,6 @@ private:
bool use_apk_expansion;
#if defined(OPENGL_ENABLED)
- bool use_16bits_fbo;
const char *gl_extensions;
#endif
diff --git a/platform/javascript/package-lock.json b/platform/javascript/package-lock.json
index 8003619576..1bc11c7ccf 100644
--- a/platform/javascript/package-lock.json
+++ b/platform/javascript/package-lock.json
@@ -109,9 +109,9 @@
"dev": true
},
"ansi-regex": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
- "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"ansi-styles": {
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index 1b9dd1e8f3..4b3804d049 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -119,6 +119,13 @@ def configure(env):
if env["bits"] == "default":
env["bits"] = "64" if is64 else "32"
+ if env["arch"] == "" and platform.machine() == "riscv64":
+ env["arch"] = "rv64"
+
+ if env["arch"] == "rv64":
+ # G = General-purpose extensions, C = Compression extension (very common).
+ env.Append(CCFLAGS=["-march=rv64gc"])
+
## Compiler configuration
if "CXX" in env and "clang" in os.path.basename(env["CXX"]):
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index c0e5aac938..212b6762e1 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -124,6 +124,7 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
#ifdef DBUS_ENABLED
case FEATURE_KEEP_SCREEN_ON:
#endif
+ case FEATURE_CLIPBOARD_PRIMARY:
return true;
default: {
}
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index c060c3d523..3048ecf39e 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -106,7 +106,6 @@ private:
int get_joy_ref(IOHIDDeviceRef p_device) const;
void poll_joypads() const;
- void setup_joypad_objects();
void config_hid_manager(CFArrayRef p_matching_array) const;
void joypad_vibration_start(int p_id, float p_magnitude, float p_duration, uint64_t p_timestamp);
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index 7945f409a1..9398573ee1 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -117,11 +117,6 @@ private:
Windows::System::Display::DisplayRequest ^ display_request;
- void _post_dpad(DWORD p_dpad, int p_device, bool p_pressed);
-
- void _drag_event(int idx, UINT uMsg, WPARAM wParam, LPARAM lParam);
- void _touch_event(int idx, UINT uMsg, WPARAM wParam, LPARAM lParam);
-
ref class ManagedType {
public:
property bool alert_close_handle;
diff --git a/scene/2d/audio_listener_2d.h b/scene/2d/audio_listener_2d.h
index 875887acc6..454053bc4a 100644
--- a/scene/2d/audio_listener_2d.h
+++ b/scene/2d/audio_listener_2d.h
@@ -43,8 +43,6 @@ private:
friend class Viewport;
protected:
- void _update_listener();
-
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 6916f832d0..28facd09ce 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -454,7 +454,7 @@ void CollisionObject2D::shape_owner_clear_shapes(uint32_t p_owner) {
}
uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const {
- ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
+ ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, UINT32_MAX);
for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
for (int i = 0; i < E.value.shapes.size(); i++) {
@@ -465,7 +465,7 @@ uint32_t CollisionObject2D::shape_find_owner(int p_shape_index) const {
}
//in theory it should be unreachable
- return 0;
+ ERR_FAIL_V_MSG(UINT32_MAX, "Can't find owner for shape index " + itos(p_shape_index) + ".");
}
void CollisionObject2D::set_pickable(bool p_enabled) {
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 271a4da705..00bfa62449 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -131,6 +131,7 @@ void CollisionPolygon2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
+ ERR_FAIL_COND(!is_inside_tree());
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
break;
}
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 54cb851216..c7742c7ba5 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -88,6 +88,8 @@ void CollisionShape2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
+ ERR_FAIL_COND(!is_inside_tree());
+
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
break;
}
diff --git a/scene/2d/cpu_particles_2d.h b/scene/2d/cpu_particles_2d.h
index 4990d443e3..391f51224e 100644
--- a/scene/2d/cpu_particles_2d.h
+++ b/scene/2d/cpu_particles_2d.h
@@ -201,7 +201,6 @@ public:
void set_explosiveness_ratio(real_t p_ratio);
void set_randomness_ratio(real_t p_ratio);
void set_lifetime_randomness(double p_random);
- void set_visibility_aabb(const Rect2 &p_aabb);
void set_use_local_coordinates(bool p_enable);
void set_speed_scale(double p_scale);
@@ -213,7 +212,6 @@ public:
real_t get_explosiveness_ratio() const;
real_t get_randomness_ratio() const;
double get_lifetime_randomness() const;
- Rect2 get_visibility_aabb() const;
bool get_use_local_coordinates() const;
double get_speed_scale() const;
@@ -226,9 +224,6 @@ public:
void set_draw_order(DrawOrder p_order);
DrawOrder get_draw_order() const;
- void set_draw_passes(int p_count);
- int get_draw_passes() const;
-
void set_texture(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture() const;
@@ -264,7 +259,6 @@ public:
void set_emission_points(const Vector<Vector2> &p_points);
void set_emission_normals(const Vector<Vector2> &p_normals);
void set_emission_colors(const Vector<Color> &p_colors);
- void set_emission_point_count(int p_count);
void set_scale_curve_x(Ref<Curve> p_scale_curve);
void set_scale_curve_y(Ref<Curve> p_scale_curve);
void set_split_scale(bool p_split_scale);
@@ -275,7 +269,6 @@ public:
Vector<Vector2> get_emission_points() const;
Vector<Vector2> get_emission_normals() const;
Vector<Color> get_emission_colors() const;
- int get_emission_point_count() const;
Ref<Curve> get_scale_curve_x() const;
Ref<Curve> get_scale_curve_y() const;
bool get_split_scale();
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index 15008390b7..58bff97da9 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -96,6 +96,10 @@ Rect2 MeshInstance2D::_edit_get_rect() const {
return Node2D::_edit_get_rect();
}
+
+bool MeshInstance2D::_edit_use_rect() const {
+ return mesh.is_valid();
+}
#endif
MeshInstance2D::MeshInstance2D() {
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index adfda4cf7f..f94d53da7d 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -48,6 +48,7 @@ protected:
public:
#ifdef TOOLS_ENABLED
virtual Rect2 _edit_get_rect() const override;
+ virtual bool _edit_use_rect() const override;
#endif
void set_mesh(const Ref<Mesh> &p_mesh);
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp
index 2f00978123..7faa964407 100644
--- a/scene/2d/navigation_agent_2d.cpp
+++ b/scene/2d/navigation_agent_2d.cpp
@@ -184,7 +184,7 @@ Vector2 NavigationAgent2D::get_target_location() const {
Vector2 NavigationAgent2D::get_next_location() {
update_navigation();
if (navigation_path.size() == 0) {
- ERR_FAIL_COND_V(agent_parent == nullptr, Vector2());
+ ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector2(), "The agent has no parent.");
return agent_parent->get_global_position();
} else {
return navigation_path[nav_path_index];
@@ -192,7 +192,7 @@ Vector2 NavigationAgent2D::get_next_location() {
}
real_t NavigationAgent2D::distance_to_target() const {
- ERR_FAIL_COND_V(agent_parent == nullptr, 0.0);
+ ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent.");
return agent_parent->get_global_position().distance_to(target_location);
}
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 41288d646f..4d4d21bad7 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1101,7 +1101,7 @@ bool CharacterBody2D::move_and_slide() {
}
if (motion_mode == MOTION_MODE_GROUNDED) {
- _move_and_slide_grounded(delta, was_on_floor, current_platform_velocity);
+ _move_and_slide_grounded(delta, was_on_floor);
} else {
_move_and_slide_free(delta);
}
@@ -1122,14 +1122,11 @@ bool CharacterBody2D::move_and_slide() {
return motion_results.size() > 0;
}
-void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) {
+void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor) {
Vector2 motion = motion_velocity * p_delta;
Vector2 motion_slide_up = motion.slide(up_direction);
Vector2 prev_floor_normal = floor_normal;
- RID prev_platform_rid = platform_rid;
- ObjectID prev_platform_object_id = platform_object_id;
- int prev_platform_layer = platform_layer;
platform_rid = RID();
platform_object_id = ObjectID();
@@ -1202,12 +1199,8 @@ void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_flo
gt.elements[2] -= result.travel;
set_global_transform(gt);
}
- on_floor = true;
- platform_rid = prev_platform_rid;
- platform_object_id = prev_platform_object_id;
- platform_layer = prev_platform_layer;
- platform_velocity = p_prev_platform_velocity;
- floor_normal = prev_floor_normal;
+ // Determines if you are on the ground.
+ _snap_on_floor(true, false);
motion_velocity = Vector2();
last_motion = Vector2();
motion = Vector2();
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
index d1f52b33f2..15e8469bb4 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -416,7 +416,7 @@ private:
MovingPlatformApplyVelocityOnLeave get_moving_platform_apply_velocity_on_leave() const;
void _move_and_slide_free(double p_delta);
- void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity);
+ void _move_and_slide_grounded(double p_delta, bool p_was_on_floor);
Ref<KinematicCollision2D> _get_slide_collision(int p_bounce);
Ref<KinematicCollision2D> _get_last_slide_collision();
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 3ac2128c2e..0a8e9e2a58 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -157,6 +157,7 @@ void RayCast2D::_notification(int p_what) {
} break;
case NOTIFICATION_DRAW: {
+ ERR_FAIL_COND(!is_inside_tree());
if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
break;
}
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b546eaefa6..e54d1cd597 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -34,6 +34,324 @@
#include "servers/navigation_server_2d.h"
+Map<Vector2i, TileSet::CellNeighbor> TileMap::TerrainConstraint::get_overlapping_coords_and_peering_bits() const {
+ Map<Vector2i, TileSet::CellNeighbor> output;
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ ERR_FAIL_COND_V(!tile_set.is_valid(), output);
+
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (bit) {
+ case 0:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ break;
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (bit) {
+ case 0:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ break;
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ // Half offset shapes.
+ TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (bit) {
+ case 0:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
+ break;
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
+ break;
+ case 4:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ } else {
+ switch (bit) {
+ case 0:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ break;
+ case 1:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
+ break;
+ case 2:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
+ break;
+ case 3:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
+ break;
+ case 4:
+ output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
+ output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
+ break;
+ default:
+ ERR_FAIL_V(output);
+ }
+ }
+ }
+ return output;
+}
+
+TileMap::TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
+ // The way we build the constraint make it easy to detect conflicting constraints.
+ tile_map = p_tile_map;
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ TileSet::TileShape shape = tile_set->get_tile_shape();
+ if (shape == TileSet::TILE_SHAPE_SQUARE) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else {
+ // Half-offset shapes
+ TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
+ if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 4;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_CORNER:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ } else {
+ switch (p_bit) {
+ case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
+ bit = 0;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
+ bit = 1;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
+ bit = 3;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
+ bit = 4;
+ base_cell_coords = p_position;
+ break;
+ case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
+ bit = 1;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
+ bit = 0;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_SIDE:
+ bit = 3;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
+ bit = 2;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
+ break;
+ case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
+ bit = 4;
+ base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
+ break;
+ default:
+ ERR_FAIL();
+ break;
+ }
+ }
+ }
+ terrain = p_terrain;
+}
+
Vector2i TileMap::transform_coords_layout(Vector2i p_coords, TileSet::TileOffsetAxis p_offset_axis, TileSet::TileLayout p_from_layout, TileSet::TileLayout p_to_layout) {
// Transform to stacked layout.
Vector2i output = p_coords;
@@ -168,7 +486,6 @@ int TileMap::get_selected_layer() const {
void TileMap::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- pending_update = true;
_clear_internals();
_recreate_internals();
} break;
@@ -305,8 +622,8 @@ String TileMap::get_layer_name(int p_layer) const {
void TileMap::set_layer_enabled(int p_layer, bool p_enabled) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].enabled = p_enabled;
- _clear_internals();
- _recreate_internals();
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
emit_signal(SNAME("changed"));
update_configuration_warnings();
@@ -320,8 +637,8 @@ bool TileMap::is_layer_enabled(int p_layer) const {
void TileMap::set_layer_modulate(int p_layer, Color p_modulate) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].modulate = p_modulate;
- _clear_internals();
- _recreate_internals();
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
emit_signal(SNAME("changed"));
}
@@ -333,8 +650,8 @@ Color TileMap::get_layer_modulate(int p_layer) const {
void TileMap::set_layer_y_sort_enabled(int p_layer, bool p_y_sort_enabled) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].y_sort_enabled = p_y_sort_enabled;
- _clear_internals();
- _recreate_internals();
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
emit_signal(SNAME("changed"));
update_configuration_warnings();
@@ -348,8 +665,8 @@ bool TileMap::is_layer_y_sort_enabled(int p_layer) const {
void TileMap::set_layer_y_sort_origin(int p_layer, int p_y_sort_origin) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].y_sort_origin = p_y_sort_origin;
- _clear_internals();
- _recreate_internals();
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
emit_signal(SNAME("changed"));
}
@@ -361,8 +678,8 @@ int TileMap::get_layer_y_sort_origin(int p_layer) const {
void TileMap::set_layer_z_index(int p_layer, int p_z_index) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
layers[p_layer].z_index = p_z_index;
- _clear_internals();
- _recreate_internals();
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
emit_signal(SNAME("changed"));
update_configuration_warnings();
@@ -486,8 +803,10 @@ void TileMap::_update_dirty_quadrants() {
}
for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ SelfList<TileMapQuadrant>::List &dirty_quadrant_list = layers[layer].dirty_quadrant_list;
+
// Update the coords cache.
- for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) {
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
q->self()->map_to_world.clear();
q->self()->world_to_map.clear();
for (Set<Vector2i>::Element *E = q->self()->cells.front(); E; E = E->next()) {
@@ -498,15 +817,18 @@ void TileMap::_update_dirty_quadrants() {
}
}
+ // Find TileData that need a runtime modification.
+ _build_runtime_update_tile_data(dirty_quadrant_list);
+
// Call the update_dirty_quadrant method on plugins.
- _rendering_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
- _physics_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
- _navigation_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
- _scenes_update_dirty_quadrants(layers[layer].dirty_quadrant_list);
+ _rendering_update_dirty_quadrants(dirty_quadrant_list);
+ _physics_update_dirty_quadrants(dirty_quadrant_list);
+ _navigation_update_dirty_quadrants(dirty_quadrant_list);
+ _scenes_update_dirty_quadrants(dirty_quadrant_list);
// Redraw the debug canvas_items.
RenderingServer *rs = RenderingServer::get_singleton();
- for (SelfList<TileMapQuadrant> *q = layers[layer].dirty_quadrant_list.first(); q; q = q->next()) {
+ for (SelfList<TileMapQuadrant> *q = dirty_quadrant_list.first(); q; q = q->next()) {
rs->canvas_item_clear(q->self()->debug_canvas_item);
Transform2D xform;
xform.set_origin(map_to_world(q->self()->coords * get_effective_quadrant_size(layer)));
@@ -519,8 +841,13 @@ void TileMap::_update_dirty_quadrants() {
}
// Clear the list
- while (layers[layer].dirty_quadrant_list.first()) {
- layers[layer].dirty_quadrant_list.remove(layers[layer].dirty_quadrant_list.first());
+ while (dirty_quadrant_list.first()) {
+ // Clear the runtime tile data.
+ for (const KeyValue<Vector2i, TileData *> &kv : dirty_quadrant_list.first()->self()->runtime_tile_data_cache) {
+ memdelete(kv.value);
+ }
+
+ dirty_quadrant_list.remove(dirty_quadrant_list.first());
}
}
@@ -529,37 +856,43 @@ void TileMap::_update_dirty_quadrants() {
_recompute_rect_cache();
}
-void TileMap::_recreate_internals() {
- for (unsigned int layer = 0; layer < layers.size(); layer++) {
- // Make sure that _clear_internals() was called prior.
- ERR_FAIL_COND_MSG(layers[layer].quadrant_map.size() > 0, "TileMap layer " + itos(layer) + " had a non-empty quadrant map.");
+void TileMap::_recreate_layer_internals(int p_layer) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
- if (!layers[layer].enabled) {
- continue;
- }
+ // Make sure that _clear_internals() was called prior.
+ ERR_FAIL_COND_MSG(layers[p_layer].quadrant_map.size() > 0, "TileMap layer " + itos(p_layer) + " had a non-empty quadrant map.");
- // Upadate the layer internals.
- _rendering_update_layer(layer);
-
- // Recreate the quadrants.
- const Map<Vector2i, TileMapCell> &tile_map = layers[layer].tile_map;
- for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
- Vector2i qk = _coords_to_quadrant_coords(layer, Vector2i(E.key.x, E.key.y));
+ if (!layers[p_layer].enabled) {
+ return;
+ }
- Map<Vector2i, TileMapQuadrant>::Element *Q = layers[layer].quadrant_map.find(qk);
- if (!Q) {
- Q = _create_quadrant(layer, qk);
- layers[layer].dirty_quadrant_list.add(&Q->get().dirty_list_element);
- }
+ // Upadate the layer internals.
+ _rendering_update_layer(p_layer);
- Vector2i pk = E.key;
- Q->get().cells.insert(pk);
+ // Recreate the quadrants.
+ const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
+ for (const KeyValue<Vector2i, TileMapCell> &E : tile_map) {
+ Vector2i qk = _coords_to_quadrant_coords(p_layer, Vector2i(E.key.x, E.key.y));
- _make_quadrant_dirty(Q);
+ Map<Vector2i, TileMapQuadrant>::Element *Q = layers[p_layer].quadrant_map.find(qk);
+ if (!Q) {
+ Q = _create_quadrant(p_layer, qk);
+ layers[p_layer].dirty_quadrant_list.add(&Q->get().dirty_list_element);
}
+
+ Vector2i pk = E.key;
+ Q->get().cells.insert(pk);
+
+ _make_quadrant_dirty(Q);
}
- _update_dirty_quadrants();
+ _queue_update_dirty_quadrants();
+}
+
+void TileMap::_recreate_internals() {
+ for (unsigned int layer = 0; layer < layers.size(); layer++) {
+ _recreate_layer_internals(layer);
+ }
}
void TileMap::_erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q) {
@@ -782,7 +1115,13 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Get the tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ const TileData *tile_data;
+ if (q.runtime_tile_data_cache.has(E_cell.value)) {
+ tile_data = q.runtime_tile_data_cache[E_cell.value];
+ } else {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ }
+
Ref<ShaderMaterial> mat = tile_data->get_material();
int z_index = tile_data->get_z_index();
@@ -829,7 +1168,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
}
// Drawing the tile in the canvas item.
- draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate);
+ draw_tile(canvas_item, E_cell.key - position, tile_set, c.source_id, c.get_atlas_coords(), c.alternative_tile, -1, modulate, tile_data);
// --- Occluders ---
for (int i = 0; i < tile_set->get_occlusion_layers_count(); i++) {
@@ -946,7 +1285,7 @@ void TileMap::_rendering_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
}
}
-void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation) {
+void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame, Color p_modulation, const TileData *p_tile_data_override) {
ERR_FAIL_COND(!p_tile_set.is_valid());
ERR_FAIL_COND(!p_tile_set->has_source(p_atlas_source_id));
ERR_FAIL_COND(!p_tile_set->get_source(p_atlas_source_id)->has_tile(p_atlas_coords));
@@ -972,7 +1311,7 @@ void TileMap::draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSe
}
// Get tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
+ const TileData *tile_data = p_tile_data_override ? p_tile_data_override : Object::cast_to<TileData>(atlas_source->get_tile_data(p_atlas_coords, p_alternative_tile));
// Get the tile modulation.
Color modulate = tile_data->get_modulate() * p_modulation;
@@ -1131,8 +1470,12 @@ void TileMap::_physics_update_dirty_quadrants(SelfList<TileMapQuadrant>::List &r
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
-
+ const TileData *tile_data;
+ if (q.runtime_tile_data_cache.has(E_cell->get())) {
+ tile_data = q.runtime_tile_data_cache[E_cell->get()];
+ } else {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ }
for (int tile_set_physics_layer = 0; tile_set_physics_layer < tile_set->get_physics_layers_count(); tile_set_physics_layer++) {
Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer);
uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer);
@@ -1324,7 +1667,12 @@ void TileMap::_navigation_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ const TileData *tile_data;
+ if (q.runtime_tile_data_cache.has(E_cell->get())) {
+ tile_data = q.runtime_tile_data_cache[E_cell->get()];
+ } else {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ }
q.navigation_regions[E_cell->get()].resize(tile_set->get_navigation_layers_count());
for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) {
@@ -1408,7 +1756,12 @@ void TileMap::_navigation_draw_quadrant_debug(TileMapQuadrant *p_quadrant) {
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ const TileData *tile_data;
+ if (p_quadrant->runtime_tile_data_cache.has(E_cell->get())) {
+ tile_data = p_quadrant->runtime_tile_data_cache[E_cell->get()];
+ } else {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+ }
Transform2D xform;
xform.set_origin(map_to_world(E_cell->get()) - quadrant_pos);
@@ -1784,6 +2137,243 @@ void TileMap::set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPat
}
}
+Set<TileSet::TerrainsPattern> TileMap::_get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints) {
+ if (!tile_set.is_valid()) {
+ return Set<TileSet::TerrainsPattern>();
+ }
+
+ // Returns all tiles compatible with the given constraints.
+ Set<TileSet::TerrainsPattern> compatible_terrain_tile_patterns;
+ for (TileSet::TerrainsPattern &terrain_pattern : tile_set->get_terrains_pattern_set(p_terrain_set)) {
+ int valid = true;
+ int in_pattern_count = 0;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+ // Check if the bit is compatible with the constraints.
+ TerrainConstraint terrain_bit_constraint = TerrainConstraint(this, p_position, bit, terrain_pattern[in_pattern_count]);
+ Set<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
+ if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
+ valid = false;
+ break;
+ }
+ in_pattern_count++;
+ }
+ }
+
+ if (valid) {
+ compatible_terrain_tile_patterns.insert(terrain_pattern);
+ }
+ }
+
+ return compatible_terrain_tile_patterns;
+}
+
+Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains) const {
+ if (!tile_set.is_valid()) {
+ return Set<TerrainConstraint>();
+ }
+
+ ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<TerrainConstraint>());
+ ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), Set<TerrainConstraint>());
+
+ // Build a set of dummy constraints get the constrained points.
+ Set<TerrainConstraint> dummy_constraints;
+ for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
+ dummy_constraints.insert(TerrainConstraint(this, E->get(), bit, -1));
+ }
+ }
+ }
+
+ // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
+ Set<TerrainConstraint> constraints;
+ for (Set<TerrainConstraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
+ TerrainConstraint c = E->get();
+
+ Map<int, int> terrain_count;
+
+ // Count the number of occurrences per terrain.
+ Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
+ for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) {
+ if (!p_to_replace.has(E_overlapping.key)) {
+ TileData *neighbor_tile_data = nullptr;
+ TileMapCell neighbor_cell = get_cell(p_layer, E_overlapping.key);
+ if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) {
+ Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id);
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile));
+ if (tile_data && tile_data->get_terrain_set() == p_terrain_set) {
+ neighbor_tile_data = tile_data;
+ }
+ }
+ }
+
+ int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping.value)) : -1;
+ if (!p_ignore_empty_terrains || terrain >= 0) {
+ if (!terrain_count.has(terrain)) {
+ terrain_count[terrain] = 0;
+ }
+ terrain_count[terrain] += 1;
+ }
+ }
+ }
+
+ // Get the terrain with the max number of occurrences.
+ int max = 0;
+ int max_terrain = -1;
+ for (const KeyValue<int, int> &E_terrain_count : terrain_count) {
+ if (E_terrain_count.value > max) {
+ max = E_terrain_count.value;
+ max_terrain = E_terrain_count.key;
+ }
+ }
+
+ // Set the adequate terrain.
+ if (max > 0) {
+ c.set_terrain(max_terrain);
+ constraints.insert(c);
+ }
+ }
+
+ return constraints;
+}
+
+Set<TileMap::TerrainConstraint> TileMap::get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const {
+ if (!tile_set.is_valid()) {
+ return Set<TerrainConstraint>();
+ }
+
+ // Compute the constraints needed from the surrounding tiles.
+ Set<TerrainConstraint> output;
+ int in_pattern_count = 0;
+ for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
+ TerrainConstraint c = TerrainConstraint(this, p_position, side, p_terrains_pattern[in_pattern_count]);
+ output.insert(c);
+ in_pattern_count++;
+ }
+ }
+
+ return output;
+}
+
+Map<Vector2i, TileSet::TerrainsPattern> TileMap::terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints) {
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileSet::TerrainsPattern>();
+ }
+
+ // Copy the constraints set.
+ Set<TerrainConstraint> constraints = p_constraints;
+
+ // Compute all acceptable patterns for each cell.
+ Map<Vector2i, Set<TileSet::TerrainsPattern>> per_cell_acceptable_tiles;
+ for (Vector2i cell : p_to_replace) {
+ per_cell_acceptable_tiles[cell] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, cell, constraints);
+ }
+
+ // Output map.
+ Map<Vector2i, TileSet::TerrainsPattern> output;
+
+ // Add all positions to a set.
+ Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
+ while (!to_replace.is_empty()) {
+ // Compute the minimum number of tile possibilities for each cell.
+ int min_nb_possibilities = 100000000;
+ for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+ min_nb_possibilities = MIN(min_nb_possibilities, E.value.size());
+ }
+
+ // Get the set of possible cells to fill, out of the most constrained ones.
+ LocalVector<Vector2i> to_choose_from;
+ for (const KeyValue<Vector2i, Set<TileSet::TerrainsPattern>> &E : per_cell_acceptable_tiles) {
+ if (E.value.size() == min_nb_possibilities) {
+ to_choose_from.push_back(E.key);
+ }
+ }
+
+ // Randomly a cell to fill out of the most constrained.
+ Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
+
+ // Get the list of acceptable pattens for the given cell.
+ Set<TileSet::TerrainsPattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
+ if (valid_tiles.is_empty()) {
+ break; // No possibilities :/
+ }
+
+ // Out of the possible patterns, prioritize the one which have the least amount of different terrains.
+ LocalVector<TileSet::TerrainsPattern> valid_tiles_with_least_amount_of_terrains;
+ int min_terrain_count = 10000;
+ LocalVector<int> terrains_counts;
+ int pattern_index = 0;
+ for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
+ Set<int> terrains;
+ for (int i = 0; i < pattern.size(); i++) {
+ terrains.insert(pattern[i]);
+ }
+ min_terrain_count = MIN(min_terrain_count, terrains.size());
+ terrains_counts.push_back(terrains.size());
+ pattern_index++;
+ }
+ pattern_index = 0;
+ for (const TileSet::TerrainsPattern &pattern : valid_tiles) {
+ if (terrains_counts[pattern_index] == min_terrain_count) {
+ valid_tiles_with_least_amount_of_terrains.push_back(pattern);
+ }
+ pattern_index++;
+ }
+
+ // Randomly select a pattern out of the remaining ones.
+ TileSet::TerrainsPattern selected_terrain_tile_pattern = valid_tiles_with_least_amount_of_terrains[Math::random(0, valid_tiles_with_least_amount_of_terrains.size() - 1)];
+
+ // Set the selected cell into the output.
+ output[selected_cell_to_replace] = selected_terrain_tile_pattern;
+ to_replace.erase(selected_cell_to_replace);
+ per_cell_acceptable_tiles.erase(selected_cell_to_replace);
+
+ // Add the new constraints from the added tiles.
+ Set<TerrainConstraint> new_constraints = get_terrain_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
+ for (Set<TerrainConstraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
+ constraints.insert(E_constraint->get());
+ }
+
+ // Compute valid tiles again for neighbors.
+ for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
+ if (is_existing_neighbor(side)) {
+ Vector2i neighbor = get_neighbor_cell(selected_cell_to_replace, side);
+ if (to_replace.has(neighbor)) {
+ per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_patterns_for_constraints(p_terrain_set, neighbor, constraints);
+ }
+ }
+ }
+ }
+ return output;
+}
+
+void TileMap::set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count());
+
+ Set<Vector2i> coords_set;
+ for (int i = 0; i < p_coords_array.size(); i++) {
+ coords_set.insert(p_coords_array[i]);
+ }
+
+ Set<TileMap::TerrainConstraint> constraints = get_terrain_constraints_from_removed_cells_list(p_layer, coords_set, p_terrain_set, p_ignore_empty_terrains);
+
+ Map<Vector2i, TileSet::TerrainsPattern> wfc_output = terrain_wave_function_collapse(coords_set, p_terrain_set, constraints);
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : wfc_output) {
+ TileMapCell cell = tile_set->get_random_tile_from_pattern(p_terrain_set, kv.value);
+ set_cell(p_layer, kv.key, cell.source_id, cell.get_atlas_coords(), cell.alternative_tile);
+ }
+}
+
TileMapCell TileMap::get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
ERR_FAIL_INDEX_V(p_layer, (int)layers.size(), TileMapCell());
const Map<Vector2i, TileMapCell> &tile_map = layers[p_layer].tile_map;
@@ -1849,6 +2439,17 @@ void TileMap::clear() {
used_rect_cache_dirty = true;
}
+void TileMap::force_update(int p_layer) {
+ if (p_layer >= 0) {
+ ERR_FAIL_INDEX(p_layer, (int)layers.size());
+ _clear_layer_internals(p_layer);
+ _recreate_layer_internals(p_layer);
+ } else {
+ _clear_internals();
+ _recreate_internals();
+ }
+}
+
void TileMap::_set_tile_data(int p_layer, const Vector<int> &p_data) {
ERR_FAIL_INDEX(p_layer, (int)layers.size());
ERR_FAIL_COND(format > FORMAT_3);
@@ -1958,6 +2559,44 @@ Vector<int> TileMap::_get_tile_data(int p_layer) const {
return data;
}
+void TileMap::_build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list) {
+ if (GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) {
+ SelfList<TileMapQuadrant> *q_list_element = r_dirty_quadrant_list.first();
+ while (q_list_element) {
+ TileMapQuadrant &q = *q_list_element->self();
+ // Iterate over the cells of the quadrant.
+ for (const KeyValue<Vector2i, Vector2i> &E_cell : q.world_to_map) {
+ TileMapCell c = get_cell(q.layer, E_cell.value, true);
+
+ TileSetSource *source;
+ if (tile_set->has_source(c.source_id)) {
+ source = *tile_set->get_source(c.source_id);
+
+ if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) {
+ continue;
+ }
+
+ TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+ if (atlas_source) {
+ bool ret = false;
+ if (GDVIRTUAL_CALL(_use_tile_data_runtime_update, q.layer, E_cell.value, ret) && ret) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile));
+
+ // Create the runtime TileData.
+ TileData *tile_data_runtime_use = tile_data->duplicate();
+ tile_data->set_allow_transform(true);
+ q.runtime_tile_data_cache[E_cell.value] = tile_data_runtime_use;
+
+ GDVIRTUAL_CALL(_tile_data_runtime_update, q.layer, E_cell.value, tile_data_runtime_use);
+ }
+ }
+ }
+ }
+ q_list_element = q_list_element->next();
+ }
+ }
+}
+
#ifdef TOOLS_ENABLED
Rect2 TileMap::_edit_get_rect() const {
// Return the visible rect of the tilemap
@@ -2989,10 +3628,14 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("map_pattern", "position_in_tilemap", "coords_in_pattern", "pattern"), &TileMap::map_pattern);
ClassDB::bind_method(D_METHOD("set_pattern", "layer", "position", "pattern"), &TileMap::set_pattern);
+ ClassDB::bind_method(D_METHOD("set_cells_from_surrounding_terrains", "layer", "cells", "terrain_set", "ignore_empty_terrains"), &TileMap::set_cells_from_surrounding_terrains, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles);
ClassDB::bind_method(D_METHOD("clear_layer", "layer"), &TileMap::clear_layer);
ClassDB::bind_method(D_METHOD("clear"), &TileMap::clear);
+ ClassDB::bind_method(D_METHOD("force_update", "layer"), &TileMap::force_update, DEFVAL(-1));
+
ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles);
ClassDB::bind_method(D_METHOD("get_used_cells", "layer"), &TileMap::get_used_cells);
@@ -3010,6 +3653,9 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileMap::_tile_set_changed_deferred_update);
+ GDVIRTUAL_BIND(_use_tile_data_runtime_update, "layer", "coords");
+ GDVIRTUAL_BIND(_tile_data_runtime_update, "layer", "coords", "tile_data");
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision_animatable"), "set_collision_animatable", "is_collision_animatable");
@@ -3030,7 +3676,7 @@ void TileMap::_bind_methods() {
void TileMap::_tile_set_changed() {
emit_signal(SNAME("changed"));
_tile_set_changed_deferred_update_needed = true;
- call_deferred("_tile_set_changed_deferred_update");
+ call_deferred(SNAME("_tile_set_changed_deferred_update"));
}
void TileMap::_tile_set_changed_deferred_update() {
@@ -3052,5 +3698,6 @@ TileMap::~TileMap() {
if (tile_set.is_valid()) {
tile_set->disconnect("changed", callable_mp(this, &TileMap::_tile_set_changed));
}
+
_clear_internals();
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index e1f38a314c..f260422290 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -79,6 +79,9 @@ struct TileMapQuadrant {
// Scenes.
Map<Vector2i, String> scenes;
+ // Runtime TileData cache.
+ Map<Vector2i, TileData *> runtime_tile_data_cache;
+
void operator=(const TileMapQuadrant &q) {
layer = q.layer;
coords = q.coords;
@@ -109,6 +112,43 @@ class TileMap : public Node2D {
GDCLASS(TileMap, Node2D);
public:
+ class TerrainConstraint {
+ private:
+ const TileMap *tile_map;
+ Vector2i base_cell_coords = Vector2i();
+ int bit = -1;
+ int terrain = -1;
+
+ public:
+ bool operator<(const TerrainConstraint &p_other) const {
+ if (base_cell_coords == p_other.base_cell_coords) {
+ return bit < p_other.bit;
+ }
+ return base_cell_coords < p_other.base_cell_coords;
+ }
+
+ String to_string() const {
+ return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
+ }
+
+ Vector2i get_base_cell_coords() const {
+ return base_cell_coords;
+ }
+
+ Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
+
+ void set_terrain(int p_terrain) {
+ terrain = p_terrain;
+ }
+
+ int get_terrain() const {
+ return terrain;
+ }
+
+ TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
+ TerrainConstraint() {}
+ };
+
enum VisibilityMode {
VISIBILITY_MODE_DEFAULT,
VISIBILITY_MODE_FORCE_SHOW,
@@ -174,6 +214,7 @@ private:
void _update_dirty_quadrants();
+ void _recreate_layer_internals(int p_layer);
void _recreate_internals();
void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q);
@@ -209,10 +250,15 @@ private:
void _scenes_cleanup_quadrant(TileMapQuadrant *p_quadrant);
void _scenes_draw_quadrant_debug(TileMapQuadrant *p_quadrant);
+ // Terrains.
+ Set<TileSet::TerrainsPattern> _get_valid_terrains_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TerrainConstraint> p_constraints);
+
// Set and get tiles from data arrays.
void _set_tile_data(int p_layer, const Vector<int> &p_data);
Vector<int> _get_tile_data(int p_layer) const;
+ void _build_runtime_update_tile_data(SelfList<TileMapQuadrant>::List &r_dirty_quadrant_list);
+
void _tile_set_changed();
bool _tile_set_changed_deferred_update_needed = false;
void _tile_set_changed_deferred_update();
@@ -242,7 +288,7 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
- static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0));
+ static void draw_tile(RID p_canvas_item, Vector2i p_position, const Ref<TileSet> p_tile_set, int p_atlas_source_id, Vector2i p_atlas_coords, int p_alternative_tile, int p_frame = -1, Color p_modulation = Color(1.0, 1.0, 1.0, 1.0), const TileData *p_tile_data_override = nullptr);
// Layers management.
int get_layers_count() const;
@@ -267,21 +313,30 @@ public:
void set_collision_animatable(bool p_enabled);
bool is_collision_animatable() const;
+ // Debug visibility modes.
void set_collision_visibility_mode(VisibilityMode p_show_collision);
VisibilityMode get_collision_visibility_mode();
void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
VisibilityMode get_navigation_visibility_mode();
+ // Cells accessors.
void set_cell(int p_layer, const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
int get_cell_source_id(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
Vector2i get_cell_atlas_coords(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
int get_cell_alternative_tile(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ // Patterns.
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
void set_pattern(int p_layer, Vector2i p_position, const Ref<TileMapPattern> p_pattern);
+ // Terrains.
+ Set<TerrainConstraint> get_terrain_constraints_from_removed_cells_list(int p_layer, const Set<Vector2i> &p_to_replace, int p_terrain_set, bool p_ignore_empty_terrains = true) const; // Not exposed.
+ Set<TerrainConstraint> get_terrain_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; // Not exposed.
+ Map<Vector2i, TileSet::TerrainsPattern> terrain_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TerrainConstraint> p_constraints); // Not exposed.
+ void set_cells_from_surrounding_terrains(int p_layer, TypedArray<Vector2i> p_coords_array, int p_terrain_set, bool p_ignore_empty_terrains = true);
+
// Not exposed to users
TileMapCell get_cell(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
Map<Vector2i, TileMapQuadrant> *get_quadrant_map(int p_layer);
@@ -312,13 +367,21 @@ public:
// Fixing a nclearing methods.
void fix_invalid_tiles();
+ // Clears tiles from a given layer
void clear_layer(int p_layer);
void clear();
- // Helpers
+ // Force a TileMap update
+ void force_update(int p_layer = -1);
+
+ // Helpers?
TypedArray<Vector2i> get_surrounding_tiles(Vector2i coords);
void draw_cells_outline(Control *p_control, Set<Vector2i> p_cells, Color p_color, Transform2D p_transform = Transform2D());
+ // Virtual function to modify the TileData at runtime
+ GDVIRTUAL2R(bool, _use_tile_data_runtime_update, int, Vector2i);
+ GDVIRTUAL3(_tile_data_runtime_update, int, Vector2i, TileData *);
+
// Configuration warnings.
TypedArray<String> get_configuration_warnings() const override;
diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp
index d411525707..9179983220 100644
--- a/scene/3d/area_3d.cpp
+++ b/scene/3d/area_3d.cpp
@@ -580,6 +580,8 @@ void Area3D::_validate_property(PropertyInfo &property) const {
property.hint_string = options;
}
+
+ CollisionObject3D::_validate_property(property);
}
void Area3D::_bind_methods() {
diff --git a/scene/3d/audio_listener_3d.h b/scene/3d/audio_listener_3d.h
index 492cacb0e9..31de3b4fb1 100644
--- a/scene/3d/audio_listener_3d.h
+++ b/scene/3d/audio_listener_3d.h
@@ -63,9 +63,6 @@ public:
virtual Transform3D get_listener_transform() const;
- void set_visible_layers(uint32_t p_layers);
- uint32_t get_visible_layers() const;
-
AudioListener3D();
~AudioListener3D();
};
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 44a685f506..261acbeeb9 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -638,6 +638,8 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const {
property.hint_string = options;
}
+
+ Node3D::_validate_property(property);
}
void AudioStreamPlayer3D::_bus_layout_changed() {
diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp
index 8e89f4fc54..5dc7382197 100644
--- a/scene/3d/bone_attachment_3d.cpp
+++ b/scene/3d/bone_attachment_3d.cpp
@@ -58,6 +58,8 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const {
property.hint_string = "";
}
}
+
+ Node3D::_validate_property(property);
}
bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) {
diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp
index 588d2b5018..af3b3ae5bc 100644
--- a/scene/3d/camera_3d.cpp
+++ b/scene/3d/camera_3d.cpp
@@ -71,6 +71,8 @@ void Camera3D::_validate_property(PropertyInfo &p_property) const {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
+
+ Node3D::_validate_property(p_property);
}
void Camera3D::_update_camera() {
diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp
index 814ed5c2a7..fd891a5e13 100644
--- a/scene/3d/collision_object_3d.cpp
+++ b/scene/3d/collision_object_3d.cpp
@@ -648,7 +648,7 @@ void CollisionObject3D::shape_owner_clear_shapes(uint32_t p_owner) {
}
uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const {
- ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, 0);
+ ERR_FAIL_INDEX_V(p_shape_index, total_subshapes, UINT32_MAX);
for (const KeyValue<uint32_t, ShapeData> &E : shapes) {
for (int i = 0; i < E.value.shapes.size(); i++) {
@@ -659,7 +659,7 @@ uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const {
}
//in theory it should be unreachable
- return 0;
+ ERR_FAIL_V_MSG(UINT32_MAX, "Can't find owner for shape index " + itos(p_shape_index) + ".");
}
CollisionObject3D::CollisionObject3D(RID p_rid, bool p_area) {
diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp
index 48ef41e015..f1e800988d 100644
--- a/scene/3d/cpu_particles_3d.cpp
+++ b/scene/3d/cpu_particles_3d.cpp
@@ -73,6 +73,7 @@ void CPUParticles3D::set_amount(int p_amount) {
}
particle_data.resize((12 + 4 + 4) * p_amount);
+ RS::get_singleton()->multimesh_set_visible_instances(multimesh, -1);
RS::get_singleton()->multimesh_allocate_data(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_3D, true, true);
particle_order.resize(p_amount);
@@ -531,6 +532,8 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const {
if (property.name.begins_with("scale_curve_") && !split_scale) {
property.usage = PROPERTY_USAGE_NONE;
}
+
+ Node3D::_validate_property(property);
}
static uint32_t idhash(uint32_t x) {
diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h
index 160814ead4..aca7328a27 100644
--- a/scene/3d/cpu_particles_3d.h
+++ b/scene/3d/cpu_particles_3d.h
@@ -209,7 +209,6 @@ public:
void set_explosiveness_ratio(real_t p_ratio);
void set_randomness_ratio(real_t p_ratio);
void set_lifetime_randomness(double p_random);
- void set_visibility_aabb(const AABB &p_aabb);
void set_use_local_coordinates(bool p_enable);
void set_speed_scale(double p_scale);
@@ -221,7 +220,6 @@ public:
real_t get_explosiveness_ratio() const;
real_t get_randomness_ratio() const;
double get_lifetime_randomness() const;
- AABB get_visibility_aabb() const;
bool get_use_local_coordinates() const;
double get_speed_scale() const;
@@ -234,9 +232,6 @@ public:
void set_draw_order(DrawOrder p_order);
DrawOrder get_draw_order() const;
- void set_draw_passes(int p_count);
- int get_draw_passes() const;
-
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;
@@ -275,7 +270,6 @@ public:
void set_emission_points(const Vector<Vector3> &p_points);
void set_emission_normals(const Vector<Vector3> &p_normals);
void set_emission_colors(const Vector<Color> &p_colors);
- void set_emission_point_count(int p_count);
void set_emission_ring_axis(Vector3 p_axis);
void set_emission_ring_height(real_t p_height);
void set_emission_ring_radius(real_t p_radius);
@@ -291,7 +285,6 @@ public:
Vector<Vector3> get_emission_points() const;
Vector<Vector3> get_emission_normals() const;
Vector<Color> get_emission_colors() const;
- int get_emission_point_count() const;
Vector3 get_emission_ring_axis() const;
real_t get_emission_ring_height() const;
real_t get_emission_ring_radius() const;
diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp
index c94a99a203..e3c63d62f9 100644
--- a/scene/3d/decal.cpp
+++ b/scene/3d/decal.cpp
@@ -160,6 +160,7 @@ void Decal::_validate_property(PropertyInfo &property) const {
if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) {
property.usage = PROPERTY_USAGE_NOEDITOR;
}
+ VisualInstance3D::_validate_property(property);
}
TypedArray<String> Decal::get_configuration_warnings() const {
diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp
index ea6242b669..13cb8b7dfb 100644
--- a/scene/3d/gpu_particles_3d.cpp
+++ b/scene/3d/gpu_particles_3d.cpp
@@ -388,6 +388,8 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const {
return;
}
}
+
+ GeometryInstance3D::_validate_property(property);
}
void GPUParticles3D::emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) {
diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp
index c787ba5087..fbdbd79c0c 100644
--- a/scene/3d/light_3d.cpp
+++ b/scene/3d/light_3d.cpp
@@ -200,21 +200,11 @@ void Light3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NOEDITOR;
}
- if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_size") {
- property.usage = PROPERTY_USAGE_NONE;
- }
-
- if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_specular") {
- property.usage = PROPERTY_USAGE_NONE;
- }
-
- if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") {
- property.usage = PROPERTY_USAGE_NONE;
- }
-
if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
+ // Angular distance is only used in DirectionalLight3D.
property.usage = PROPERTY_USAGE_NONE;
}
+ VisualInstance3D::_validate_property(property);
}
void Light3D::_bind_methods() {
@@ -361,6 +351,7 @@ Light3D::~Light3D() {
void DirectionalLight3D::set_shadow_mode(ShadowMode p_mode) {
shadow_mode = p_mode;
RS::get_singleton()->light_directional_set_shadow_mode(light, RS::LightDirectionalShadowMode(p_mode));
+ notify_property_list_changed();
}
DirectionalLight3D::ShadowMode DirectionalLight3D::get_shadow_mode() const {
@@ -385,6 +376,25 @@ bool DirectionalLight3D::is_sky_only() const {
return sky_only;
}
+void DirectionalLight3D::_validate_property(PropertyInfo &property) const {
+ if (shadow_mode == SHADOW_ORTHOGONAL && (property.name == "directional_shadow_split_1" || property.name == "directional_shadow_blend_splits")) {
+ // Split 2 and split blending are only used with the PSSM 2 Splits and PSSM 4 Splits shadow modes.
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if ((shadow_mode == SHADOW_ORTHOGONAL || shadow_mode == SHADOW_PARALLEL_2_SPLITS) && (property.name == "directional_shadow_split_2" || property.name == "directional_shadow_split_3")) {
+ // Splits 3 and 4 are only used with the PSSM 4 Splits shadow mode.
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+
+ if (property.name == "light_size" || property.name == "light_projector" || property.name == "light_specular") {
+ // Not implemented in DirectionalLight3D (`light_size` is replaced by `light_angular_distance`).
+ property.usage = PROPERTY_USAGE_NONE;
+ }
+
+ Light3D::_validate_property(property);
+}
+
void DirectionalLight3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight3D::set_shadow_mode);
ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight3D::get_shadow_mode);
@@ -400,8 +410,8 @@ void DirectionalLight3D::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_2", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_2_OFFSET);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET);
- ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled");
+ ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,8192,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_RANGE, "0,1024,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE);
diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h
index f788c323f7..a9f5ce27b4 100644
--- a/scene/3d/light_3d.h
+++ b/scene/3d/light_3d.h
@@ -152,6 +152,7 @@ private:
protected:
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
public:
void set_shadow_mode(ShadowMode p_mode);
diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp
index b56f0fd145..3bcb6add76 100644
--- a/scene/3d/lightmap_gi.cpp
+++ b/scene/3d/lightmap_gi.cpp
@@ -1370,6 +1370,7 @@ void LightmapGI::_validate_property(PropertyInfo &property) const {
if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) {
property.usage = PROPERTY_USAGE_NONE;
}
+ VisualInstance3D::_validate_property(property);
}
void LightmapGI::_bind_methods() {
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index c2d5c757db..1bc7d20c19 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -192,7 +192,7 @@ Vector3 NavigationAgent3D::get_target_location() const {
Vector3 NavigationAgent3D::get_next_location() {
update_navigation();
if (navigation_path.size() == 0) {
- ERR_FAIL_COND_V(agent_parent == nullptr, Vector3());
+ ERR_FAIL_COND_V_MSG(agent_parent == nullptr, Vector3(), "The agent has no parent.");
return agent_parent->get_global_transform().origin;
} else {
return navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0);
@@ -200,7 +200,7 @@ Vector3 NavigationAgent3D::get_next_location() {
}
real_t NavigationAgent3D::distance_to_target() const {
- ERR_FAIL_COND_V(agent_parent == nullptr, 0.0);
+ ERR_FAIL_COND_V_MSG(agent_parent == nullptr, 0.0, "The agent has no parent.");
return agent_parent->get_global_transform().origin.distance_to(target_location);
}
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 8a51a259f7..473368cf69 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -162,7 +162,7 @@ void _bake_navigation_mesh(void *p_user_data) {
}
void NavigationRegion3D::bake_navigation_mesh() {
- ERR_FAIL_COND(bake_thread.is_started());
+ ERR_FAIL_COND_MSG(bake_thread.is_started(), "Unable to start another bake request. The navigation mesh bake thread is already baking a navigation mesh.");
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
args->nav_region = this;
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index c96204cf60..8a39d4d30f 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -220,6 +220,13 @@ void Node3D::_notification(int p_what) {
}
}
+void Node3D::set_basis(const Basis &p_basis) {
+ set_transform(Transform3D(p_basis, data.local_transform.origin));
+}
+void Node3D::set_quaternion(const Quaternion &p_quaternion) {
+ set_transform(Transform3D(Basis(p_quaternion), data.local_transform.origin));
+}
+
void Node3D::set_transform(const Transform3D &p_transform) {
data.local_transform = p_transform;
data.dirty |= DIRTY_VECTORS;
@@ -229,6 +236,13 @@ void Node3D::set_transform(const Transform3D &p_transform) {
}
}
+Basis Node3D::get_basis() const {
+ return get_transform().basis;
+}
+Quaternion Node3D::get_quaternion() const {
+ return Quaternion(get_transform().basis);
+}
+
void Node3D::set_global_transform(const Transform3D &p_transform) {
Transform3D xform =
(data.parent && !data.top_level_active) ?
@@ -308,6 +322,45 @@ void Node3D::set_position(const Vector3 &p_position) {
}
}
+void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
+ if (data.rotation_edit_mode == p_mode) {
+ return;
+ }
+ data.rotation_edit_mode = p_mode;
+ notify_property_list_changed();
+}
+
+Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const {
+ return data.rotation_edit_mode;
+}
+
+void Node3D::set_rotation_order(RotationOrder p_order) {
+ Basis::EulerOrder order = Basis::EulerOrder(p_order);
+
+ if (data.rotation_order == order) {
+ return;
+ }
+
+ ERR_FAIL_INDEX(int32_t(order), 6);
+
+ if (data.dirty & DIRTY_VECTORS) {
+ data.rotation = data.local_transform.basis.get_euler_normalized(order);
+ data.scale = data.local_transform.basis.get_scale();
+ data.dirty &= ~DIRTY_VECTORS;
+ } else {
+ data.rotation = Basis::from_euler(data.rotation, data.rotation_order).get_euler_normalized(order);
+ }
+
+ data.rotation_order = order;
+ //changing rotation order should not affect transform
+
+ notify_property_list_changed(); //will change rotation
+}
+
+Node3D::RotationOrder Node3D::get_rotation_order() const {
+ return RotationOrder(data.rotation_order);
+}
+
void Node3D::set_rotation(const Vector3 &p_euler_rad) {
if (data.dirty & DIRTY_VECTORS) {
data.scale = data.local_transform.basis.get_scale();
@@ -324,7 +377,7 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) {
void Node3D::set_scale(const Vector3 &p_scale) {
if (data.dirty & DIRTY_VECTORS) {
- data.rotation = data.local_transform.basis.get_rotation();
+ data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
data.dirty &= ~DIRTY_VECTORS;
}
@@ -343,7 +396,7 @@ Vector3 Node3D::get_position() const {
Vector3 Node3D::get_rotation() const {
if (data.dirty & DIRTY_VECTORS) {
data.scale = data.local_transform.basis.get_scale();
- data.rotation = data.local_transform.basis.get_rotation();
+ data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
data.dirty &= ~DIRTY_VECTORS;
}
@@ -354,7 +407,7 @@ Vector3 Node3D::get_rotation() const {
Vector3 Node3D::get_scale() const {
if (data.dirty & DIRTY_VECTORS) {
data.scale = data.local_transform.basis.get_scale();
- data.rotation = data.local_transform.basis.get_rotation();
+ data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
data.dirty &= ~DIRTY_VECTORS;
}
@@ -484,14 +537,14 @@ void Node3D::_update_gizmos() {
#endif
}
-#ifdef TOOLS_ENABLED
void Node3D::set_disable_gizmos(bool p_enabled) {
+#ifdef TOOLS_ENABLED
data.gizmos_disabled = p_enabled;
if (!p_enabled) {
clear_gizmos();
}
-}
#endif
+}
void Node3D::set_disable_scale(bool p_enabled) {
data.disable_scale = p_enabled;
@@ -780,6 +833,24 @@ NodePath Node3D::get_visibility_parent() const {
return visibility_parent_path;
}
+void Node3D::_validate_property(PropertyInfo &property) const {
+ if (data.rotation_edit_mode != ROTATION_EDIT_MODE_BASIS && property.name == "basis") {
+ property.usage = 0;
+ }
+ if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && property.name == "scale") {
+ property.usage = 0;
+ }
+ if (data.rotation_edit_mode != ROTATION_EDIT_MODE_QUATERNION && property.name == "quaternion") {
+ property.usage = 0;
+ }
+ if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation") {
+ property.usage = 0;
+ }
+ if (data.rotation_edit_mode != ROTATION_EDIT_MODE_EULER && property.name == "rotation_order") {
+ property.usage = 0;
+ }
+}
+
void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform);
ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform);
@@ -787,8 +858,16 @@ void Node3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_position"), &Node3D::get_position);
ClassDB::bind_method(D_METHOD("set_rotation", "euler"), &Node3D::set_rotation);
ClassDB::bind_method(D_METHOD("get_rotation"), &Node3D::get_rotation);
+ ClassDB::bind_method(D_METHOD("set_rotation_order", "order"), &Node3D::set_rotation_order);
+ ClassDB::bind_method(D_METHOD("get_rotation_order"), &Node3D::get_rotation_order);
+ ClassDB::bind_method(D_METHOD("set_rotation_edit_mode", "edit_mode"), &Node3D::set_rotation_edit_mode);
+ ClassDB::bind_method(D_METHOD("get_rotation_edit_mode"), &Node3D::get_rotation_edit_mode);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node3D::set_scale);
ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale);
+ ClassDB::bind_method(D_METHOD("set_quaternion", "quaternion"), &Node3D::set_quaternion);
+ ClassDB::bind_method(D_METHOD("get_quaternion"), &Node3D::get_quaternion);
+ ClassDB::bind_method(D_METHOD("set_basis", "basis"), &Node3D::set_basis);
+ ClassDB::bind_method(D_METHOD("get_basis"), &Node3D::get_basis);
ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform);
ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform);
ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d);
@@ -848,15 +927,29 @@ void Node3D::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_EXIT_WORLD);
BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED);
+ BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_EULER);
+ BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_QUATERNION);
+ BIND_ENUM_CONSTANT(ROTATION_EDIT_MODE_BASIS);
+
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_XYZ);
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_XZY);
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_YXZ);
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_YZX);
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_ZXY);
+ BIND_ENUM_CONSTANT(ROTATION_ORDER_ZYX);
+
//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM3D,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ;
ADD_GROUP("Transform", "");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "quaternion", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_quaternion", "get_quaternion");
+ ADD_PROPERTY(PropertyInfo(Variant::BASIS, "basis", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_basis", "get_basis");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level");
- ADD_GROUP("Matrix", "");
- ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_transform", "get_transform");
ADD_GROUP("Visibility", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent");
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index d6dcdd96fe..3e21eb12be 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -51,6 +51,23 @@ class Node3D : public Node {
GDCLASS(Node3D, Node);
OBJ_CATEGORY("3D");
+public:
+ enum RotationEditMode {
+ ROTATION_EDIT_MODE_EULER,
+ ROTATION_EDIT_MODE_QUATERNION,
+ ROTATION_EDIT_MODE_BASIS,
+ };
+
+ enum RotationOrder {
+ ROTATION_ORDER_XYZ,
+ ROTATION_ORDER_XZY,
+ ROTATION_ORDER_YXZ,
+ ROTATION_ORDER_YZX,
+ ROTATION_ORDER_ZXY,
+ ROTATION_ORDER_ZYX
+ };
+
+private:
enum TransformDirty {
DIRTY_NONE = 0,
DIRTY_VECTORS = 1,
@@ -63,8 +80,10 @@ class Node3D : public Node {
struct Data {
mutable Transform3D global_transform;
mutable Transform3D local_transform;
+ mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ;
mutable Vector3 rotation;
mutable Vector3 scale = Vector3(1, 1, 1);
+ mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
mutable int dirty = DIRTY_NONE;
@@ -116,6 +135,8 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const override;
+
public:
enum {
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED,
@@ -130,17 +151,28 @@ public:
Ref<World3D> get_world_3d() const;
void set_position(const Vector3 &p_position);
+
+ void set_rotation_edit_mode(RotationEditMode p_mode);
+ RotationEditMode get_rotation_edit_mode() const;
+
+ void set_rotation_order(RotationOrder p_order);
void set_rotation(const Vector3 &p_euler_rad);
void set_scale(const Vector3 &p_scale);
Vector3 get_position() const;
+
+ RotationOrder get_rotation_order() const;
Vector3 get_rotation() const;
Vector3 get_scale() const;
void set_transform(const Transform3D &p_transform);
+ void set_basis(const Basis &p_basis);
+ void set_quaternion(const Quaternion &p_quaternion);
void set_global_transform(const Transform3D &p_transform);
Transform3D get_transform() const;
+ Basis get_basis() const;
+ Quaternion get_quaternion() const;
Transform3D get_global_transform() const;
#ifdef TOOLS_ENABLED
@@ -214,4 +246,7 @@ public:
Node3D();
};
+VARIANT_ENUM_CAST(Node3D::RotationEditMode)
+VARIANT_ENUM_CAST(Node3D::RotationOrder)
+
#endif // NODE_3D_H
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 9ea37e4bfa..a0eac76e39 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -248,6 +248,7 @@ void PathFollow3D::_validate_property(PropertyInfo &property) const {
property.hint_string = "0," + rtos(max) + ",0.01,or_lesser,or_greater";
}
+ Node3D::_validate_property(property);
}
TypedArray<String> PathFollow3D::get_configuration_warnings() const {
diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp
index 8cb348d6e3..4f1003839e 100644
--- a/scene/3d/physics_body_3d.cpp
+++ b/scene/3d/physics_body_3d.cpp
@@ -1059,6 +1059,7 @@ void RigidDynamicBody3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
}
+ PhysicsBody3D::_validate_property(property);
}
RigidDynamicBody3D::RigidDynamicBody3D() :
@@ -1933,6 +1934,7 @@ void CharacterBody3D::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
}
+ PhysicsBody3D::_validate_property(property);
}
CharacterBody3D::CharacterBody3D() :
@@ -3060,7 +3062,7 @@ void PhysicalBone3D::set_joint_rotation(const Vector3 &p_euler_rad) {
}
Vector3 PhysicalBone3D::get_joint_rotation() const {
- return joint_offset.basis.get_rotation();
+ return joint_offset.basis.get_euler_normalized();
}
const Transform3D &PhysicalBone3D::get_body_offset() const {
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index 719dbedd94..29c382cd05 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -188,6 +188,7 @@ void ReflectionProbe::_validate_property(PropertyInfo &property) const {
property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
}
}
+ VisualInstance3D::_validate_property(property);
}
void ReflectionProbe::_bind_methods() {
diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp
index d5fb1fa6ab..d890609e23 100644
--- a/scene/3d/remote_transform_3d.cpp
+++ b/scene/3d/remote_transform_3d.cpp
@@ -68,7 +68,7 @@ void RemoteTransform3D::_update_remote() {
Transform3D our_trans = get_global_transform();
if (update_remote_rotation) {
- n->set_rotation(our_trans.basis.get_rotation());
+ n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
}
if (update_remote_scale) {
@@ -90,7 +90,7 @@ void RemoteTransform3D::_update_remote() {
Transform3D our_trans = get_transform();
if (update_remote_rotation) {
- n->set_rotation(our_trans.basis.get_rotation());
+ n->set_rotation(our_trans.basis.get_euler_normalized(Basis::EulerOrder(n->get_rotation_order())));
}
if (update_remote_scale) {
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index fbc3767151..e3744ad5e9 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -178,32 +178,32 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
}
void Skeleton3D::_validate_property(PropertyInfo &property) const {
- PackedStringArray spr = property.name.split("/");
- if (spr.size() == 3 && spr[0] == "bones") {
- if (spr[2] == "rest") {
+ PackedStringArray split = property.name.split("/");
+ if (split.size() == 3 && split[0] == "bones") {
+ if (split[2] == "rest") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
if (is_show_rest_only()) {
- if (spr[2] == "enabled") {
+ if (split[2] == "enabled") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- if (spr[2] == "position") {
+ if (split[2] == "position") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- if (spr[2] == "rotation") {
+ if (split[2] == "rotation") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- if (spr[2] == "scale") {
+ if (split[2] == "scale") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- } else if (!is_bone_enabled(spr[1].to_int())) {
- if (spr[2] == "position") {
+ } else if (!is_bone_enabled(split[1].to_int())) {
+ if (split[2] == "position") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- if (spr[2] == "rotation") {
+ if (split[2] == "rotation") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
- if (spr[2] == "scale") {
+ if (split[2] == "scale") {
property.usage |= PROPERTY_USAGE_READ_ONLY;
}
}
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index 2e788051f4..1498955ec0 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -351,6 +351,8 @@ void SkeletonIK3D::_validate_property(PropertyInfo &property) const {
property.hint_string = "";
}
}
+
+ Node::_validate_property(property);
}
void SkeletonIK3D::_bind_methods() {
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 4cf08e7c99..ccb25bcd4c 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -137,8 +137,7 @@ class SkeletonIK3D : public Node {
FabrikInverseKinematic::Task *task = nullptr;
protected:
- virtual void
- _validate_property(PropertyInfo &property) const override;
+ virtual void _validate_property(PropertyInfo &property) const override;
static void _bind_methods();
virtual void _notification(int p_what);
diff --git a/scene/3d/soft_dynamic_body_3d.cpp b/scene/3d/soft_dynamic_body_3d.cpp
index 9fceb21790..903eedb58b 100644
--- a/scene/3d/soft_dynamic_body_3d.cpp
+++ b/scene/3d/soft_dynamic_body_3d.cpp
@@ -250,7 +250,7 @@ void SoftDynamicBody3D::_notification(int p_what) {
RID space = get_world_3d()->get_space();
PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, space);
- prepare_physics_server();
+ _prepare_physics_server();
} break;
case NOTIFICATION_READY: {
@@ -284,13 +284,13 @@ void SoftDynamicBody3D::_notification(int p_what) {
case NOTIFICATION_DISABLED: {
if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
- prepare_physics_server();
+ _prepare_physics_server();
}
} break;
case NOTIFICATION_ENABLED: {
if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
- prepare_physics_server();
+ _prepare_physics_server();
}
} break;
@@ -378,7 +378,7 @@ void SoftDynamicBody3D::_bind_methods() {
TypedArray<String> SoftDynamicBody3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if (get_mesh().is_null()) {
+ if (mesh.is_null()) {
warnings.push_back(TTR("This body will be ignored until you set a mesh."));
}
@@ -407,11 +407,17 @@ void SoftDynamicBody3D::_update_physics_server() {
}
void SoftDynamicBody3D::_draw_soft_mesh() {
- if (get_mesh().is_null()) {
+ if (mesh.is_null()) {
return;
}
- const RID mesh_rid = get_mesh()->get_rid();
+ RID mesh_rid = mesh->get_rid();
+ if (owned_mesh != mesh_rid) {
+ _become_mesh_owner();
+ mesh_rid = mesh->get_rid();
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);
+ }
+
if (!rendering_server_handler.is_ready(mesh_rid)) {
rendering_server_handler.prepare(mesh_rid, 0);
@@ -430,11 +436,11 @@ void SoftDynamicBody3D::_draw_soft_mesh() {
rendering_server_handler.commit_changes();
}
-void SoftDynamicBody3D::prepare_physics_server() {
+void SoftDynamicBody3D::_prepare_physics_server() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- if (get_mesh().is_valid()) {
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid());
+ if (mesh.is_valid()) {
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh->get_rid());
} else {
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
}
@@ -443,9 +449,13 @@ void SoftDynamicBody3D::prepare_physics_server() {
}
#endif
- if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) {
- become_mesh_owner();
- PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh()->get_rid());
+ if (mesh.is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) {
+ RID mesh_rid = mesh->get_rid();
+ if (owned_mesh != mesh_rid) {
+ _become_mesh_owner();
+ mesh_rid = mesh->get_rid();
+ }
+ PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, mesh_rid);
RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftDynamicBody3D::_draw_soft_mesh));
} else {
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, RID());
@@ -455,38 +465,32 @@ void SoftDynamicBody3D::prepare_physics_server() {
}
}
-void SoftDynamicBody3D::become_mesh_owner() {
- if (mesh.is_null()) {
- return;
- }
+void SoftDynamicBody3D::_become_mesh_owner() {
+ Vector<Ref<Material>> copy_materials;
+ copy_materials.append_array(surface_override_materials);
- if (!mesh_owner) {
- mesh_owner = true;
+ ERR_FAIL_COND(!mesh->get_surface_count());
- Vector<Ref<Material>> copy_materials;
- copy_materials.append_array(surface_override_materials);
+ // Get current mesh array and create new mesh array with necessary flag for SoftDynamicBody
+ Array surface_arrays = mesh->surface_get_arrays(0);
+ Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
+ Dictionary surface_lods = mesh->surface_get_lods(0);
+ uint32_t surface_format = mesh->surface_get_format(0);
- ERR_FAIL_COND(!mesh->get_surface_count());
+ surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
- // Get current mesh array and create new mesh array with necessary flag for SoftDynamicBody
- Array surface_arrays = mesh->surface_get_arrays(0);
- Array surface_blend_arrays = mesh->surface_get_blend_shape_arrays(0);
- Dictionary surface_lods = mesh->surface_get_lods(0);
- uint32_t surface_format = mesh->surface_get_format(0);
+ Ref<ArrayMesh> soft_mesh;
+ soft_mesh.instantiate();
+ soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format);
+ soft_mesh->surface_set_material(0, mesh->surface_get_material(0));
- surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE;
+ set_mesh(soft_mesh);
- Ref<ArrayMesh> soft_mesh;
- soft_mesh.instantiate();
- soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format);
- soft_mesh->surface_set_material(0, mesh->surface_get_material(0));
-
- set_mesh(soft_mesh);
-
- for (int i = copy_materials.size() - 1; 0 <= i; --i) {
- set_surface_override_material(i, copy_materials[i]);
- }
+ for (int i = copy_materials.size() - 1; 0 <= i; --i) {
+ set_surface_override_material(i, copy_materials[i]);
}
+
+ owned_mesh = soft_mesh->get_rid();
}
void SoftDynamicBody3D::set_collision_mask(uint32_t p_mask) {
@@ -548,16 +552,10 @@ void SoftDynamicBody3D::set_disable_mode(DisableMode p_mode) {
return;
}
- bool inside_tree = is_inside_tree();
-
- if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) {
- prepare_physics_server();
- }
-
disable_mode = p_mode;
- if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) {
- prepare_physics_server();
+ if (mesh.is_valid() && is_inside_tree() && !is_enabled()) {
+ _prepare_physics_server();
}
}
diff --git a/scene/3d/soft_dynamic_body_3d.h b/scene/3d/soft_dynamic_body_3d.h
index 5e7fbfe29e..57e116aa05 100644
--- a/scene/3d/soft_dynamic_body_3d.h
+++ b/scene/3d/soft_dynamic_body_3d.h
@@ -90,7 +90,7 @@ private:
DisableMode disable_mode = DISABLE_MODE_REMOVE;
- bool mesh_owner = false;
+ RID owned_mesh;
uint32_t collision_mask = 1;
uint32_t collision_layer = 1;
NodePath parent_collision_ignore;
@@ -106,6 +106,12 @@ private:
void _update_pickable();
+ void _update_physics_server();
+ void _draw_soft_mesh();
+
+ void _prepare_physics_server();
+ void _become_mesh_owner();
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -120,14 +126,7 @@ protected:
TypedArray<String> get_configuration_warnings() const override;
-protected:
- void _update_physics_server();
- void _draw_soft_mesh();
-
public:
- void prepare_physics_server();
- void become_mesh_owner();
-
RID get_physics_rid() const { return physics_rid; }
void set_collision_mask(uint32_t p_mask);
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 349a534680..90af70e7c2 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -743,6 +743,8 @@ void Sprite3D::_validate_property(PropertyInfo &property) const {
if (property.name == "frame_coords") {
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ SpriteBase3D::_validate_property(property);
}
void Sprite3D::_bind_methods() {
@@ -1015,6 +1017,8 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const {
}
property.usage |= PROPERTY_USAGE_KEYING_INCREMENTS;
}
+
+ SpriteBase3D::_validate_property(property);
}
void AnimatedSprite3D::_notification(int p_what) {
diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h
index 90c2a309e1..61448c0e32 100644
--- a/scene/3d/sprite_3d.h
+++ b/scene/3d/sprite_3d.h
@@ -118,12 +118,6 @@ public:
void set_flip_v(bool p_flip);
bool is_flipped_v() const;
- void set_region_enabled(bool p_region);
- bool is_region_enabled() const;
-
- void set_region_rect(const Rect2 &p_region_rect);
- Rect2 get_region_rect() const;
-
void set_modulate(const Color &p_color);
Color get_modulate() const;
diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp
index 9dbee58f0e..a16820cbdc 100644
--- a/scene/3d/xr_nodes.cpp
+++ b/scene/3d/xr_nodes.cpp
@@ -261,6 +261,8 @@ void XRNode3D::_validate_property(PropertyInfo &property) const {
}
property.hint_string = hint_string;
}
+
+ Node3D::_validate_property(property);
}
void XRNode3D::set_tracker(const StringName p_tracker_name) {
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 1f000d5f39..26caf826a7 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -166,6 +166,8 @@ void AnimationPlayer::_validate_property(PropertyInfo &property) const {
property.hint_string = hint;
}
+
+ Node::_validate_property(property);
}
void AnimationPlayer::_get_property_list(List<PropertyInfo> *p_list) const {
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 21e309efe0..d9d88b5510 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -301,7 +301,6 @@ public:
void set_current_animation(const String &p_anim);
String get_assigned_animation() const;
void set_assigned_animation(const String &p_anim);
- void stop_all();
void set_active(bool p_active);
bool is_active() const;
bool is_valid() const;
diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h
index 21b49937a7..5abea39d20 100644
--- a/scene/animation/animation_tree.h
+++ b/scene/animation/animation_tree.h
@@ -57,8 +57,6 @@ public:
Vector<Input> inputs;
- real_t process_input(int p_input, real_t p_time, bool p_seek, real_t p_blend);
-
friend class AnimationTree;
struct AnimationState {
@@ -85,7 +83,6 @@ public:
State *state = nullptr;
real_t _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, real_t p_time, bool p_seek, const Vector<StringName> &p_connections);
- void _pre_update_animations(HashMap<NodePath, int> *track_map);
//all this is temporary
StringName base_path;
@@ -110,8 +107,6 @@ protected:
void _validate_property(PropertyInfo &property) const override;
- void _set_parent(Object *p_parent);
-
GDVIRTUAL0RC(Dictionary, _get_child_nodes)
GDVIRTUAL0RC(Array, _get_parameter_list)
GDVIRTUAL1RC(Ref<AnimationNode>, _get_child_by_name, StringName)
@@ -265,7 +260,6 @@ private:
AnimationNode::State state;
bool cache_valid = false;
void _node_removed(Node *p_node);
- void _caches_cleared();
void _clear_caches();
bool _update_caches(AnimationPlayer *player);
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index d2fd65d3d9..5065f21604 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -292,6 +292,8 @@ void AudioStreamPlayer::_validate_property(PropertyInfo &property) const {
property.hint_string = options;
}
+
+ Node::_validate_property(property);
}
void AudioStreamPlayer::_bus_layout_changed() {
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index e253a27e66..9818c8f0cc 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -111,9 +111,18 @@ void Button::_notification(int p_what) {
if (!flat) {
style->draw(ci, Rect2(Point2(0, 0), size));
}
- color = get_theme_color(SNAME("font_color"));
- if (has_theme_color(SNAME("icon_normal_color"))) {
- color_icon = get_theme_color(SNAME("icon_normal_color"));
+
+ // Focus colors only take precedence over normal state.
+ if (has_focus()) {
+ color = get_theme_color(SNAME("font_focus_color"));
+ if (has_theme_color(SNAME("icon_focus_color"))) {
+ color_icon = get_theme_color(SNAME("icon_focus_color"));
+ }
+ } else {
+ color = get_theme_color(SNAME("font_color"));
+ if (has_theme_color(SNAME("icon_normal_color"))) {
+ color_icon = get_theme_color(SNAME("icon_normal_color"));
+ }
}
} break;
case DRAW_HOVER_PRESSED: {
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c1db684d9b..046d256867 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -129,7 +129,7 @@ void CodeEdit::_notification(int p_what) {
}
const int scroll_width = code_completion_options_count > code_completion_max_lines ? code_completion_scroll_width : 0;
- const int code_completion_base_width = font->get_string_size(code_completion_base).width;
+ const int code_completion_base_width = font->get_string_size(code_completion_base, font_size).width;
if (caret_pos.x - code_completion_base_width + code_completion_rect.size.width + scroll_width > get_size().width) {
code_completion_rect.position.x = get_size().width - code_completion_rect.size.width - scroll_width;
} else {
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 973074397b..77a1efd021 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -400,7 +400,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const {
usage |= PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_CHECKED;
}
- p_list->push_back(PropertyInfo(Variant::INT, "theme_override_font_sizes/" + E, PROPERTY_HINT_NONE, "", usage));
+ p_list->push_back(PropertyInfo(Variant::INT, "theme_override_font_sizes/" + E, PROPERTY_HINT_RANGE, "1,256,1,or_greater", usage));
}
}
{
@@ -637,7 +637,9 @@ void Control::_notification(int p_notification) {
}
} else {
//is a regular root control or top_level
- data.RI = get_viewport()->_gui_add_root_control(this);
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ data.RI = viewport->_gui_add_root_control(this);
}
data.parent_canvas_item = get_parent_item();
@@ -646,7 +648,9 @@ void Control::_notification(int p_notification) {
data.parent_canvas_item->connect("item_rect_changed", callable_mp(this, &Control::_size_changed));
} else {
//connect viewport
- get_viewport()->connect("size_changed", callable_mp(this, &Control::_size_changed));
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
}
} break;
case NOTIFICATION_EXIT_CANVAS: {
@@ -655,7 +659,9 @@ void Control::_notification(int p_notification) {
data.parent_canvas_item = nullptr;
} else if (!is_set_as_top_level()) {
//disconnect viewport
- get_viewport()->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->disconnect("size_changed", callable_mp(this, &Control::_size_changed));
}
if (data.RI) {
@@ -979,7 +985,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String
Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const {
if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
const Ref<Font> *font = data.font_override.getptr(p_name);
- if (font) {
+ if (font && (*font)->get_data_count() > 0) {
return *font;
}
}
@@ -992,7 +998,7 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_
int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const {
if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) {
const int *font_size = data.font_size_override.getptr(p_name);
- if (font_size) {
+ if (font_size && (*font_size) > 0) {
return *font_size;
}
}
@@ -2580,16 +2586,6 @@ void Control::warp_mouse(const Point2 &p_to_pos) {
}
bool Control::is_text_field() const {
- /*
- if (get_script_instance()) {
- Variant v=p_point;
- const Variant *p[2]={&v,&p_data};
- Callable::CallError ce;
- Variant ret = get_script_instance()->call("is_text_field",p,2,ce);
- if (ce.error==Callable::CallError::CALL_OK)
- return ret;
- }
- */
return false;
}
diff --git a/scene/gui/control.h b/scene/gui/control.h
index b551a2e299..02ab336ef0 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -233,9 +233,6 @@ private:
static constexpr unsigned properties_managed_by_container_count = 11;
static String properties_managed_by_container[properties_managed_by_container_count];
- // used internally
- Control *_find_control_at_pos(CanvasItem *p_node, const Point2 &p_pos, const Transform2D &p_xform, Transform2D &r_inv_xform);
-
void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
Control *_get_focus_neighbor(Side p_side, int p_count = 0);
@@ -250,7 +247,6 @@ private:
void _update_minimum_size();
void _clear_size_warning();
- void _update_scroll();
void _compute_offsets(Rect2 p_rect, const real_t p_anchors[4], real_t (&r_offsets)[4]);
void _compute_anchors(Rect2 p_rect, const real_t p_offsets[4], real_t (&r_anchors)[4]);
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 973b72973d..bb77da9548 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -51,26 +51,32 @@ VBoxContainer *FileDialog::get_vbox() {
void FileDialog::_theme_changed() {
Color font_color = vbox->get_theme_color(SNAME("font_color"), SNAME("Button"));
Color font_hover_color = vbox->get_theme_color(SNAME("font_hover_color"), SNAME("Button"));
+ Color font_focus_color = vbox->get_theme_color(SNAME("font_focus_color"), SNAME("Button"));
Color font_pressed_color = vbox->get_theme_color(SNAME("font_pressed_color"), SNAME("Button"));
dir_up->add_theme_color_override("icon_normal_color", font_color);
dir_up->add_theme_color_override("icon_hover_color", font_hover_color);
+ dir_up->add_theme_color_override("icon_focus_color", font_focus_color);
dir_up->add_theme_color_override("icon_pressed_color", font_pressed_color);
dir_prev->add_theme_color_override("icon_color_normal", font_color);
dir_prev->add_theme_color_override("icon_color_hover", font_hover_color);
+ dir_prev->add_theme_color_override("icon_focus_color", font_focus_color);
dir_prev->add_theme_color_override("icon_color_pressed", font_pressed_color);
dir_next->add_theme_color_override("icon_color_normal", font_color);
dir_next->add_theme_color_override("icon_color_hover", font_hover_color);
+ dir_next->add_theme_color_override("icon_focus_color", font_focus_color);
dir_next->add_theme_color_override("icon_color_pressed", font_pressed_color);
refresh->add_theme_color_override("icon_normal_color", font_color);
refresh->add_theme_color_override("icon_hover_color", font_hover_color);
+ refresh->add_theme_color_override("icon_focus_color", font_focus_color);
refresh->add_theme_color_override("icon_pressed_color", font_pressed_color);
show_hidden->add_theme_color_override("icon_normal_color", font_color);
show_hidden->add_theme_color_override("icon_hover_color", font_hover_color);
+ show_hidden->add_theme_color_override("icon_focus_color", font_focus_color);
show_hidden->add_theme_color_override("icon_pressed_color", font_pressed_color);
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 9c285d3be2..8c33d306a1 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -235,7 +235,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable()) {
+ if (is_middle_mouse_paste_enabled() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_MIDDLE && is_editable() && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
String paste_buffer = DisplayServer::get_singleton()->clipboard_get_primary().strip_escapes();
deselect();
@@ -290,7 +290,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
selection.double_click = true;
last_dblclk = 0;
caret_column = selection.begin;
- if (!pass) {
+ if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(text);
}
} else if (b->is_double_click()) {
@@ -308,7 +308,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
break;
}
}
- if (!pass) {
+ if (!pass && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin));
}
}
@@ -328,7 +328,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
update();
} else {
- if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (selection.enabled && !pass && b->get_button_index() == MOUSE_BUTTON_LEFT && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(text.substr(selection.begin, selection.end - selection.begin));
}
if (!text.is_empty() && is_editable() && clear_button_enabled) {
@@ -2293,33 +2293,33 @@ void LineEdit::_ensure_menu() {
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
- menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED);
- menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO);
- menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR);
- menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL);
- menu->add_child(menu_dir);
+ menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
+ menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
+ menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
+ menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
+ menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
menu_ctl->set_name("CTLMenu");
- menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM);
- menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM);
- menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE);
- menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE);
- menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO);
- menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO);
- menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF);
+ menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
+ menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
+ menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
+ menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
+ menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
+ menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
+ menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
menu_ctl->add_separator();
- menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM);
- menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI);
- menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI);
- menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI);
- menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI);
+ menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
+ menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
+ menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
+ menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
+ menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
menu_ctl->add_separator();
- menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ);
- menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
- menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ);
- menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
- menu->add_child(menu_ctl);
+ menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
+ menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
+ menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
+ menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
+ menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option));
@@ -2346,12 +2346,12 @@ void LineEdit::_ensure_menu() {
menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
}
menu->add_separator();
- menu->add_submenu_item(RTR("Text writing direction"), "DirMenu");
+ menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu");
menu->add_separator();
- menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC);
+ menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
if (editable) {
- menu->add_submenu_item(RTR("Insert control character"), "CTLMenu");
+ menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu");
}
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED);
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO);
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 951e179f7d..3364e02e01 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -190,7 +190,6 @@ private:
void _toggle_draw_caret();
void clear_internal();
- void changed_internal();
void _editor_settings_changed();
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 925e6f5b97..c3201186ea 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -168,7 +168,12 @@ void LinkButton::_notification(int p_what) {
switch (get_draw_mode()) {
case DRAW_NORMAL: {
- color = get_theme_color(SNAME("font_color"));
+ if (has_focus()) {
+ color = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ color = get_theme_color(SNAME("font_color"));
+ }
+
do_underline = underline_mode == UNDERLINE_MODE_ALWAYS;
} break;
case DRAW_HOVER_PRESSED:
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 2adeb2d947..c00c040048 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -71,7 +71,11 @@ void OptionButton::_notification(int p_what) {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index c7090e7231..8458a75eef 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -80,7 +80,6 @@ protected:
virtual Size2 _get_contents_minimum_size() const override;
public:
- void set_child_rect(Control *p_child);
PopupPanel();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 76d8491710..f1efbbda98 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1602,14 +1602,16 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
selection.to_char = words[i + 1];
selection.active = true;
- DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ }
update();
break;
}
}
}
} else if (!b->is_pressed()) {
- if (selection.enabled) {
+ if (selection.enabled && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
selection.click_item = nullptr;
@@ -2821,12 +2823,12 @@ bool RichTextLabel::is_scroll_following() const {
return scroll_follow;
}
-Error RichTextLabel::parse_bbcode(const String &p_bbcode) {
+void RichTextLabel::parse_bbcode(const String &p_bbcode) {
clear();
- return append_text(p_bbcode);
+ append_text(p_bbcode);
}
-Error RichTextLabel::append_text(const String &p_bbcode) {
+void RichTextLabel::append_text(const String &p_bbcode) {
int pos = 0;
List<String> tag_stack;
@@ -3549,8 +3551,6 @@ Error RichTextLabel::append_text(const String &p_bbcode) {
break;
}
}
-
- return OK;
}
void RichTextLabel::scroll_to_paragraph(int p_paragraph) {
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index efcff6ab32..6d772876ad 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -555,8 +555,8 @@ public:
void set_deselect_on_focus_loss_enabled(const bool p_enabled);
bool is_deselect_on_focus_loss_enabled() const;
- Error parse_bbcode(const String &p_bbcode);
- Error append_text(const String &p_bbcode);
+ void parse_bbcode(const String &p_bbcode);
+ void append_text(const String &p_bbcode);
void set_use_bbcode(bool p_enable);
bool is_using_bbcode() const;
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 1d84d6dbc8..411a62b1d9 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -83,7 +83,6 @@ private:
Vector<Tab> tabs;
int current = 0;
int previous = 0;
- int _get_top_margin() const;
TabAlign tab_align = ALIGN_CENTER;
bool clip_tabs = true;
int rb_hover = -1;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 2befd5b44f..cb7a6c0978 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1538,7 +1538,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
- if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MOUSE_BUTTON_MIDDLE) {
+ if (is_middle_mouse_paste_enabled() && mb->get_button_index() == MOUSE_BUTTON_MIDDLE && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
paste_primary_clipboard();
}
@@ -1579,7 +1579,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
dragging_selection = false;
can_drag_minimap = false;
click_select_held->stop();
- DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ }
}
// Notify to show soft keyboard.
@@ -5185,7 +5187,7 @@ void TextEdit::_paste_internal() {
}
void TextEdit::_paste_primary_clipboard_internal() {
- if (!is_editable()) {
+ if (!is_editable() || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
return;
}
@@ -5211,32 +5213,32 @@ void TextEdit::_generate_context_menu() {
menu_dir = memnew(PopupMenu);
menu_dir->set_name("DirMenu");
- menu_dir->add_radio_check_item(RTR("Same as layout direction"), MENU_DIR_INHERITED);
- menu_dir->add_radio_check_item(RTR("Auto-detect direction"), MENU_DIR_AUTO);
- menu_dir->add_radio_check_item(RTR("Left-to-right"), MENU_DIR_LTR);
- menu_dir->add_radio_check_item(RTR("Right-to-left"), MENU_DIR_RTL);
+ menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED);
+ menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO);
+ menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR);
+ menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL);
menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT);
menu_ctl = memnew(PopupMenu);
menu_ctl->set_name("CTLMenu");
- menu_ctl->add_item(RTR("Left-to-right mark (LRM)"), MENU_INSERT_LRM);
- menu_ctl->add_item(RTR("Right-to-left mark (RLM)"), MENU_INSERT_RLM);
- menu_ctl->add_item(RTR("Start of left-to-right embedding (LRE)"), MENU_INSERT_LRE);
- menu_ctl->add_item(RTR("Start of right-to-left embedding (RLE)"), MENU_INSERT_RLE);
- menu_ctl->add_item(RTR("Start of left-to-right override (LRO)"), MENU_INSERT_LRO);
- menu_ctl->add_item(RTR("Start of right-to-left override (RLO)"), MENU_INSERT_RLO);
- menu_ctl->add_item(RTR("Pop direction formatting (PDF)"), MENU_INSERT_PDF);
+ menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM);
+ menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM);
+ menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE);
+ menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE);
+ menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO);
+ menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO);
+ menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF);
menu_ctl->add_separator();
- menu_ctl->add_item(RTR("Arabic letter mark (ALM)"), MENU_INSERT_ALM);
- menu_ctl->add_item(RTR("Left-to-right isolate (LRI)"), MENU_INSERT_LRI);
- menu_ctl->add_item(RTR("Right-to-left isolate (RLI)"), MENU_INSERT_RLI);
- menu_ctl->add_item(RTR("First strong isolate (FSI)"), MENU_INSERT_FSI);
- menu_ctl->add_item(RTR("Pop direction isolate (PDI)"), MENU_INSERT_PDI);
+ menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM);
+ menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI);
+ menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI);
+ menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI);
+ menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI);
menu_ctl->add_separator();
- menu_ctl->add_item(RTR("Zero width joiner (ZWJ)"), MENU_INSERT_ZWJ);
- menu_ctl->add_item(RTR("Zero width non-joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
- menu_ctl->add_item(RTR("Word joiner (WJ)"), MENU_INSERT_WJ);
- menu_ctl->add_item(RTR("Soft hyphen (SHY)"), MENU_INSERT_SHY);
+ menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ);
+ menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ);
+ menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ);
+ menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY);
menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT);
menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option));
@@ -5264,12 +5266,12 @@ void TextEdit::_generate_context_menu() {
menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : 0);
}
menu->add_separator();
- menu->add_submenu_item(RTR("Text writing direction"), "DirMenu");
+ menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu");
menu->add_separator();
- menu->add_check_item(RTR("Display control characters"), MENU_DISPLAY_UCC);
+ menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC);
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
if (editable) {
- menu->add_submenu_item(RTR("Insert control character"), "CTLMenu");
+ menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu");
}
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED);
menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO);
@@ -5538,7 +5540,9 @@ void TextEdit::_update_selection_mode_word() {
}
}
- DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ }
update();
@@ -5567,7 +5571,9 @@ void TextEdit::_update_selection_mode_line() {
set_caret_column(0);
select(selection.selecting_line, selection.selecting_column, line, col);
- DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
+ DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
+ }
update();
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 3f041bf65a..1245a37c4d 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3570,7 +3570,9 @@ int Tree::_get_title_button_height() const {
void Tree::_notification(int p_what) {
if (p_what == NOTIFICATION_FOCUS_ENTER) {
- focus_in_id = get_viewport()->get_processed_events_count();
+ if (get_viewport()) {
+ focus_in_id = get_viewport()->get_processed_events_count();
+ }
}
if (p_what == NOTIFICATION_MOUSE_EXIT) {
if (cache.hover_type != Cache::CLICK_NONE) {
@@ -4035,7 +4037,7 @@ int Tree::get_column_minimum_width(int p_column) const {
// Check if the visible title of the column is wider.
if (show_column_titles) {
- min_width = MAX(cache.font->get_string_size(columns[p_column].title).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
+ min_width = MAX(cache.font->get_string_size(columns[p_column].title, cache.font_size).width + cache.bg->get_margin(SIDE_LEFT) + cache.bg->get_margin(SIDE_RIGHT), min_width);
}
if (!columns[p_column].clip_content) {
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index 916833c9a7..5065684839 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -892,9 +892,9 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("draw_primitive", "points", "colors", "uvs", "texture", "width"), &CanvasItem::draw_primitive, DEFVAL(Ref<Texture2D>()), DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("draw_polygon", "points", "colors", "uvs", "texture"), &CanvasItem::draw_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
ClassDB::bind_method(D_METHOD("draw_colored_polygon", "points", "color", "uvs", "texture"), &CanvasItem::draw_colored_polygon, DEFVAL(PackedVector2Array()), DEFVAL(Ref<Texture2D>()));
- ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
+ ClassDB::bind_method(D_METHOD("draw_string", "font", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &CanvasItem::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_char", "font", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &CanvasItem::draw_char, DEFVAL(""), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
ClassDB::bind_method(D_METHOD("draw_mesh", "mesh", "texture", "transform", "modulate"), &CanvasItem::draw_mesh, DEFVAL(Transform2D()), DEFVAL(Color(1, 1, 1, 1)));
ClassDB::bind_method(D_METHOD("draw_multimesh", "multimesh", "texture"), &CanvasItem::draw_multimesh);
ClassDB::bind_method(D_METHOD("draw_set_transform", "position", "rotation", "scale"), &CanvasItem::draw_set_transform, DEFVAL(0.0), DEFVAL(Size2(1.0, 1.0)));
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index ba9f47119d..04376ad809 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -34,10 +34,10 @@
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/canvas_item_material.h"
+#include "scene/resources/font.h"
#include "servers/text_server.h"
class CanvasLayer;
-class Font;
class MultiMesh;
class StyleBox;
class Window;
@@ -235,9 +235,9 @@ public:
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);
- void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
+ void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ real_t draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, const String &p_next = "", int p_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
void draw_set_transform_matrix(const Transform2D &p_matrix);
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 4540e42b4c..26bff4494b 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -128,7 +128,7 @@ void CanvasLayer::_notification(int p_what) {
} else {
vp = Node::get_viewport();
}
- ERR_FAIL_COND(!vp);
+ ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
vp->_canvas_layer_add(this);
viewport = vp->get_viewport_rid();
@@ -140,6 +140,8 @@ void CanvasLayer::_notification(int p_what) {
} break;
case NOTIFICATION_EXIT_TREE: {
+ ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
+
vp->_canvas_layer_remove(this);
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
@@ -160,6 +162,8 @@ Size2 CanvasLayer::get_viewport_size() const {
return Size2(1, 1);
}
+ ERR_FAIL_NULL_V_MSG(vp, Size2(1, 1), "Viewport is not initialized.");
+
Rect2 r = vp->get_visible_rect();
return r.size;
}
@@ -169,7 +173,7 @@ RID CanvasLayer::get_viewport() const {
}
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
- ERR_FAIL_NULL(p_viewport);
+ ERR_FAIL_NULL_MSG(p_viewport, "Cannot set viewport to nullptr.");
if (is_inside_tree()) {
vp->_canvas_layer_remove(this);
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index 19331c1906..7a4f9f9c52 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -136,7 +136,6 @@ private:
void _flush_ugc();
_FORCE_INLINE_ void _update_group_order(Group &g, bool p_use_priority = false);
- void _update_listener();
Array _get_nodes_in_group(const StringName &p_group);
@@ -265,9 +264,6 @@ public:
void set_pause(bool p_enabled);
bool is_paused() const;
- void set_camera(const RID &p_camera);
- RID get_camera() const;
-
#ifdef DEBUG_ENABLED
void set_debug_collisions_hint(bool p_enabled);
bool is_debugging_collisions_hint() const;
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 0e62e6e30a..3280190250 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2192,7 +2192,10 @@ void Viewport::_drop_physics_mouseover(bool p_paused_only) {
if (physics_object_over.is_valid()) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
if (co) {
- if (!(p_paused_only && co->can_process())) {
+ if (!co->is_inside_tree()) {
+ physics_object_over = ObjectID();
+ physics_object_capture = ObjectID();
+ } else if (!(p_paused_only && co->can_process())) {
co->_mouse_exit();
physics_object_over = ObjectID();
physics_object_capture = ObjectID();
@@ -2213,7 +2216,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
Object *o = ObjectDB::get_instance(E->key());
if (o) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
- if (co) {
+ if (co && co->is_inside_tree()) {
if (p_clean_all_frames && p_paused_only && co->can_process()) {
continue;
}
@@ -2239,7 +2242,7 @@ void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paus
Object *o = ObjectDB::get_instance(E->key().first);
if (o) {
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
- if (co) {
+ if (co && co->is_inside_tree()) {
if (p_clean_all_frames && p_paused_only && co->can_process()) {
continue;
}
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 08b78a39b1..f8e4845ae6 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -30,13 +30,40 @@
#include "animation.h"
+#include "core/io/marshalls.h"
#include "core/math/geometry_3d.h"
#include "scene/scene_string_names.h"
bool Animation::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
- if (name.begins_with("tracks/")) {
+ if (p_name == SNAME("_compression")) {
+ ERR_FAIL_COND_V(tracks.size() > 0, false); //can only set compression if no tracks exist
+ Dictionary comp = p_value;
+ ERR_FAIL_COND_V(!comp.has("fps"), false);
+ ERR_FAIL_COND_V(!comp.has("bounds"), false);
+ ERR_FAIL_COND_V(!comp.has("pages"), false);
+ ERR_FAIL_COND_V(!comp.has("format_version"), false);
+ uint32_t format_version = comp["format_version"];
+ ERR_FAIL_COND_V(format_version > Compression::FORMAT_VERSION, false); // version does not match this supported version
+ compression.fps = comp["fps"];
+ Array bounds = comp["bounds"];
+ compression.bounds.resize(bounds.size());
+ for (int i = 0; i < bounds.size(); i++) {
+ compression.bounds[i] = bounds[i];
+ }
+ Array pages = comp["pages"];
+ compression.pages.resize(pages.size());
+ for (int i = 0; i < pages.size(); i++) {
+ Dictionary page = pages[i];
+ ERR_FAIL_COND_V(!page.has("data"), false);
+ ERR_FAIL_COND_V(!page.has("time_offset"), false);
+ compression.pages[i].data = page["data"];
+ compression.pages[i].time_offset = page["time_offset"];
+ }
+ compression.enabled = true;
+ return true;
+ } else if (name.begins_with("tracks/")) {
int track = name.get_slicec('/', 1).to_int();
String what = name.get_slicec('/', 2);
@@ -72,6 +99,34 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
if (what == "path") {
track_set_path(track, p_value);
+ } else if (what == "compressed_track") {
+ int index = p_value;
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)index, compression.bounds.size(), false);
+ Track *t = tracks[track];
+ t->interpolation = INTERPOLATION_LINEAR; //only linear supported
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ tt->compressed_track = index;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ rt->compressed_track = index;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ st->compressed_track = index;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ bst->compressed_track = index;
+ } break;
+ default: {
+ return false;
+ }
+ }
+ return true;
} else if (what == "interp") {
track_set_interpolation_type(track, InterpolationType(p_value.operator int()));
} else if (what == "loop_wrap") {
@@ -369,7 +424,30 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
- if (name == "length") {
+ if (p_name == SNAME("_compression")) {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ Dictionary comp;
+ comp["fps"] = compression.fps;
+ Array bounds;
+ bounds.resize(compression.bounds.size());
+ for (uint32_t i = 0; i < compression.bounds.size(); i++) {
+ bounds[i] = compression.bounds[i];
+ }
+ comp["bounds"] = bounds;
+ Array pages;
+ pages.resize(compression.pages.size());
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ Dictionary page;
+ page["data"] = compression.pages[i].data;
+ page["time_offset"] = compression.pages[i].time_offset;
+ pages[i] = page;
+ }
+ comp["pages"] = pages;
+ comp["format_version"] = Compression::FORMAT_VERSION;
+
+ r_ret = comp;
+ return true;
+ } else if (name == "length") {
r_ret = length;
} else if (name == "loop") {
r_ret = loop;
@@ -414,6 +492,34 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
} else if (what == "path") {
r_ret = track_get_path(track);
+ } else if (what == "compressed_track") {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ Track *t = tracks[track];
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ r_ret = tt->compressed_track;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ r_ret = rt->compressed_track;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ r_ret = st->compressed_track;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ r_ret = bst->compressed_track;
+ } break;
+ default: {
+ r_ret = Variant();
+ ERR_FAIL_V(false);
+ }
+ }
+
+ return true;
+
} else if (what == "interp") {
r_ret = track_get_interpolation_type(track);
} else if (what == "loop_wrap") {
@@ -692,14 +798,21 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
}
void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (compression.enabled) {
+ p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ }
for (int i = 0; i < tracks.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
- p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::NODE_PATH, "tracks/" + itos(i) + "/path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ if (track_is_compressed(i)) {
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/compressed_track", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ } else {
+ p_list->push_back(PropertyInfo(Variant::INT, "tracks/" + itos(i) + "/interp", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/loop_wrap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tracks/" + itos(i) + "/keys", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ }
}
}
@@ -765,21 +878,25 @@ void Animation::remove_track(int p_track) {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_MSG(tt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(tt->positions);
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_MSG(rt->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(rt->rotations);
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_MSG(st->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(st->scales);
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND_MSG(bst->compressed_track >= 0, "Compressed tracks can't be manually removed. Call clear() to get rid of compression first.");
_clear(bst->blend_shapes);
} break;
@@ -837,9 +954,9 @@ NodePath Animation::track_get_path(int p_track) const {
return tracks[p_track]->path;
}
-int Animation::find_track(const NodePath &p_path) const {
+int Animation::find_track(const NodePath &p_path, const TrackType p_type) const {
for (int i = 0; i < tracks.size(); i++) {
- if (tracks[i]->path == p_path) {
+ if (tracks[i]->path == p_path && tracks[i]->type == p_type) {
return i;
}
};
@@ -907,6 +1024,8 @@ int Animation::position_track_insert_key(int p_track, double p_time, const Vecto
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND_V(tt->compressed_track >= 0, -1);
+
TKey<Vector3> tkey;
tkey.time = p_time;
tkey.value = p_position;
@@ -922,6 +1041,19 @@ Error Animation::position_track_get_key(int p_track, int p_key, Vector3 *r_posit
PositionTrack *tt = static_cast<PositionTrack *>(t);
ERR_FAIL_COND_V(t->type != TYPE_POSITION_3D, ERR_INVALID_PARAMETER);
+
+ if (tt->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_position = _uncompress_pos_scale(tt->compressed_track, key);
+ return OK;
+ }
+
ERR_FAIL_INDEX_V(p_key, tt->positions.size(), ERR_INVALID_PARAMETER);
*r_position = tt->positions[p_key].value;
@@ -936,6 +1068,14 @@ Error Animation::position_track_interpolate(int p_track, double p_time, Vector3
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ if (_pos_scale_interpolate_compressed(tt->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
bool ok = false;
Vector3 tk = _interpolate(tt->positions, p_time, tt->interpolation, tt->loop_wrap, &ok);
@@ -956,6 +1096,8 @@ int Animation::rotation_track_insert_key(int p_track, double p_time, const Quate
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND_V(rt->compressed_track >= 0, -1);
+
TKey<Quaternion> tkey;
tkey.time = p_time;
tkey.value = p_rotation;
@@ -971,6 +1113,19 @@ Error Animation::rotation_track_get_key(int p_track, int p_key, Quaternion *r_ro
RotationTrack *rt = static_cast<RotationTrack *>(t);
ERR_FAIL_COND_V(t->type != TYPE_ROTATION_3D, ERR_INVALID_PARAMETER);
+
+ if (rt->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_rotation = _uncompress_quaternion(key);
+ return OK;
+ }
+
ERR_FAIL_INDEX_V(p_key, rt->rotations.size(), ERR_INVALID_PARAMETER);
*r_rotation = rt->rotations[p_key].value;
@@ -985,6 +1140,14 @@ Error Animation::rotation_track_interpolate(int p_track, double p_time, Quaterni
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ if (_rotation_interpolate_compressed(rt->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
bool ok = false;
Quaternion tk = _interpolate(rt->rotations, p_time, rt->interpolation, rt->loop_wrap, &ok);
@@ -1005,6 +1168,8 @@ int Animation::scale_track_insert_key(int p_track, double p_time, const Vector3
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND_V(st->compressed_track >= 0, -1);
+
TKey<Vector3> tkey;
tkey.time = p_time;
tkey.value = p_scale;
@@ -1020,6 +1185,19 @@ Error Animation::scale_track_get_key(int p_track, int p_key, Vector3 *r_scale) c
ScaleTrack *st = static_cast<ScaleTrack *>(t);
ERR_FAIL_COND_V(t->type != TYPE_SCALE_3D, ERR_INVALID_PARAMETER);
+
+ if (st->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_scale = _uncompress_pos_scale(st->compressed_track, key);
+ return OK;
+ }
+
ERR_FAIL_INDEX_V(p_key, st->scales.size(), ERR_INVALID_PARAMETER);
*r_scale = st->scales[p_key].value;
@@ -1034,6 +1212,14 @@ Error Animation::scale_track_interpolate(int p_track, double p_time, Vector3 *r_
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ if (_pos_scale_interpolate_compressed(st->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
+
bool ok = false;
Vector3 tk = _interpolate(st->scales, p_time, st->interpolation, st->loop_wrap, &ok);
@@ -1052,6 +1238,8 @@ int Animation::blend_shape_track_insert_key(int p_track, double p_time, float p_
BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND_V(st->compressed_track >= 0, -1);
+
TKey<float> tkey;
tkey.time = p_time;
tkey.value = p_blend_shape;
@@ -1065,11 +1253,24 @@ Error Animation::blend_shape_track_get_key(int p_track, int p_key, float *r_blen
ERR_FAIL_INDEX_V(p_track, tracks.size(), ERR_INVALID_PARAMETER);
Track *t = tracks[p_track];
- BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_key, st->blend_shapes.size(), ERR_INVALID_PARAMETER);
- *r_blend_shape = st->blend_shapes[p_key].value;
+ if (bst->compressed_track >= 0) {
+ Vector3i key;
+ double time;
+ bool fetch_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key, key, time);
+ if (!fetch_success) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ *r_blend_shape = _uncompress_blend_shape(key);
+ return OK;
+ }
+
+ ERR_FAIL_INDEX_V(p_key, bst->blend_shapes.size(), ERR_INVALID_PARAMETER);
+
+ *r_blend_shape = bst->blend_shapes[p_key].value;
return OK;
}
@@ -1079,11 +1280,19 @@ Error Animation::blend_shape_track_interpolate(int p_track, double p_time, float
Track *t = tracks[p_track];
ERR_FAIL_COND_V(t->type != TYPE_BLEND_SHAPE, ERR_INVALID_PARAMETER);
- BlendShapeTrack *st = static_cast<BlendShapeTrack *>(t);
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+
+ if (bst->compressed_track >= 0) {
+ if (_blend_shape_interpolate_compressed(bst->compressed_track, p_time, *r_interpolation)) {
+ return OK;
+ } else {
+ return ERR_UNAVAILABLE;
+ }
+ }
bool ok = false;
- float tk = _interpolate(st->blend_shapes, p_time, st->interpolation, st->loop_wrap, &ok);
+ float tk = _interpolate(bst->blend_shapes, p_time, bst->interpolation, bst->loop_wrap, &ok);
if (!ok) {
return ERR_UNAVAILABLE;
@@ -1105,24 +1314,36 @@ void Animation::track_remove_key(int p_track, int p_idx) {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ ERR_FAIL_COND(tt->compressed_track >= 0);
+
ERR_FAIL_INDEX(p_idx, tt->positions.size());
tt->positions.remove(p_idx);
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ ERR_FAIL_COND(rt->compressed_track >= 0);
+
ERR_FAIL_INDEX(p_idx, rt->rotations.size());
rt->rotations.remove(p_idx);
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ ERR_FAIL_COND(st->compressed_track >= 0);
+
ERR_FAIL_INDEX(p_idx, st->scales.size());
st->scales.remove(p_idx);
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+
+ ERR_FAIL_COND(bst->compressed_track >= 0);
+
ERR_FAIL_INDEX(p_idx, bst->blend_shapes.size());
bst->blend_shapes.remove(p_idx);
@@ -1169,6 +1390,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+
+ if (tt->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(tt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
int k = _find(tt->positions, p_time);
if (k < 0 || k >= tt->positions.size()) {
return -1;
@@ -1181,6 +1417,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+
+ if (rt->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(rt->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
int k = _find(rt->rotations, p_time);
if (k < 0 || k >= rt->rotations.size()) {
return -1;
@@ -1193,6 +1444,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+
+ if (st->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<3>(st->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
int k = _find(st->scales, p_time);
if (k < 0 || k >= st->scales.size()) {
return -1;
@@ -1205,6 +1471,21 @@ int Animation::track_find_key(int p_track, double p_time, bool p_exact) const {
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+
+ if (bst->compressed_track >= 0) {
+ double time;
+ double time_next;
+ Vector3i key;
+ Vector3i key_next;
+ uint32_t key_index;
+ bool fetch_compressed_success = _fetch_compressed<1>(bst->compressed_track, p_time, key, time, key_next, time_next, &key_index);
+ ERR_FAIL_COND_V(!fetch_compressed_success, -1);
+ if (p_exact && time != p_time) {
+ return -1;
+ }
+ return key_index;
+ }
+
int k = _find(bst->blend_shapes, p_time);
if (k < 0 || k >= bst->blend_shapes.size()) {
return -1;
@@ -1392,18 +1673,30 @@ int Animation::track_get_key_count(int p_track) const {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ return _get_compressed_key_count(tt->compressed_track);
+ }
return tt->positions.size();
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ return _get_compressed_key_count(rt->compressed_track);
+ }
return rt->rotations.size();
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ return _get_compressed_key_count(st->compressed_track);
+ }
return st->scales.size();
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ return _get_compressed_key_count(bst->compressed_track);
+ }
return bst->blend_shapes.size();
} break;
case TYPE_VALUE: {
@@ -1438,28 +1731,24 @@ Variant Animation::track_get_key_value(int p_track, int p_key_idx) const {
switch (t->type) {
case TYPE_POSITION_3D: {
- PositionTrack *tt = static_cast<PositionTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), Variant());
-
- return tt->positions[p_key_idx].value;
+ Vector3 value;
+ position_track_get_key(p_track, p_key_idx, &value);
+ return value;
} break;
case TYPE_ROTATION_3D: {
- RotationTrack *rt = static_cast<RotationTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), Variant());
-
- return rt->rotations[p_key_idx].value;
+ Quaternion value;
+ rotation_track_get_key(p_track, p_key_idx, &value);
+ return value;
} break;
case TYPE_SCALE_3D: {
- ScaleTrack *st = static_cast<ScaleTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), Variant());
-
- return st->scales[p_key_idx].value;
+ Vector3 value;
+ scale_track_get_key(p_track, p_key_idx, &value);
+ return value;
} break;
case TYPE_BLEND_SHAPE: {
- BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
- ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), Variant());
-
- return bst->blend_shapes[p_key_idx].value;
+ float value;
+ blend_shape_track_get_key(p_track, p_key_idx, &value);
+ return value;
} break;
case TYPE_VALUE: {
ValueTrack *vt = static_cast<ValueTrack *>(t);
@@ -1520,21 +1809,49 @@ double Animation::track_get_key_time(int p_track, int p_key_idx) const {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(tt->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
return tt->positions[p_key_idx].time;
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(rt->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
return rt->rotations[p_key_idx].time;
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<3>(st->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
return st->scales[p_key_idx].time;
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ Vector3i value;
+ double time;
+ bool fetch_compressed_success = _fetch_compressed_by_index<1>(bst->compressed_track, p_key_idx, value, time);
+ ERR_FAIL_COND_V(!fetch_compressed_success, false);
+ return time;
+ }
ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
return bst->blend_shapes[p_key_idx].time;
} break;
@@ -1580,6 +1897,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
TKey<Vector3> key = tt->positions[p_key_idx];
key.time = p_time;
@@ -1589,6 +1907,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
}
case TYPE_ROTATION_3D: {
RotationTrack *tt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->rotations.size());
TKey<Quaternion> key = tt->rotations[p_key_idx];
key.time = p_time;
@@ -1598,6 +1917,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
}
case TYPE_SCALE_3D: {
ScaleTrack *tt = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->scales.size());
TKey<Vector3> key = tt->scales[p_key_idx];
key.time = p_time;
@@ -1607,6 +1927,7 @@ void Animation::track_set_key_time(int p_track, int p_key_idx, double p_time) {
}
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *tt = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->blend_shapes.size());
TKey<float> key = tt->blend_shapes[p_key_idx];
key.time = p_time;
@@ -1671,21 +1992,33 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ if (tt->compressed_track >= 0) {
+ return 1.0;
+ }
ERR_FAIL_INDEX_V(p_key_idx, tt->positions.size(), -1);
return tt->positions[p_key_idx].transition;
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ if (rt->compressed_track >= 0) {
+ return 1.0;
+ }
ERR_FAIL_INDEX_V(p_key_idx, rt->rotations.size(), -1);
return rt->rotations[p_key_idx].transition;
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ if (st->compressed_track >= 0) {
+ return 1.0;
+ }
ERR_FAIL_INDEX_V(p_key_idx, st->scales.size(), -1);
return st->scales[p_key_idx].transition;
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ if (bst->compressed_track >= 0) {
+ return 1.0;
+ }
ERR_FAIL_INDEX_V(p_key_idx, bst->blend_shapes.size(), -1);
return bst->blend_shapes[p_key_idx].transition;
} break;
@@ -1715,6 +2048,35 @@ real_t Animation::track_get_key_transition(int p_track, int p_key_idx) const {
ERR_FAIL_V(0);
}
+bool Animation::track_is_compressed(int p_track) const {
+ ERR_FAIL_INDEX_V(p_track, tracks.size(), false);
+ Track *t = tracks[p_track];
+
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ return tt->compressed_track >= 0;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ return rt->compressed_track >= 0;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ return st->compressed_track >= 0;
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ return bst->compressed_track >= 0;
+ } break;
+ default: {
+ return false; //animation does not really use transitions
+ } break;
+ }
+
+ ERR_FAIL_V(false);
+}
+
void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p_value) {
ERR_FAIL_INDEX(p_track, tracks.size());
Track *t = tracks[p_track];
@@ -1723,6 +2085,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_POSITION_3D: {
ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
tt->positions.write[p_key_idx].value = p_value;
@@ -1731,6 +2094,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_ROTATION_3D: {
ERR_FAIL_COND((p_value.get_type() != Variant::QUATERNION) && (p_value.get_type() != Variant::BASIS));
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND(rt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
rt->rotations.write[p_key_idx].value = p_value;
@@ -1739,6 +2103,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_SCALE_3D: {
ERR_FAIL_COND((p_value.get_type() != Variant::VECTOR3) && (p_value.get_type() != Variant::VECTOR3I));
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND(st->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, st->scales.size());
st->scales.write[p_key_idx].value = p_value;
@@ -1747,6 +2112,7 @@ void Animation::track_set_key_value(int p_track, int p_key_idx, const Variant &p
case TYPE_BLEND_SHAPE: {
ERR_FAIL_COND((p_value.get_type() != Variant::FLOAT) && (p_value.get_type() != Variant::INT));
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND(bst->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
bst->blend_shapes.write[p_key_idx].value = p_value;
@@ -1820,21 +2186,25 @@ void Animation::track_set_key_transition(int p_track, int p_key_idx, real_t p_tr
switch (t->type) {
case TYPE_POSITION_3D: {
PositionTrack *tt = static_cast<PositionTrack *>(t);
+ ERR_FAIL_COND(tt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, tt->positions.size());
tt->positions.write[p_key_idx].transition = p_transition;
} break;
case TYPE_ROTATION_3D: {
RotationTrack *rt = static_cast<RotationTrack *>(t);
+ ERR_FAIL_COND(rt->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, rt->rotations.size());
rt->rotations.write[p_key_idx].transition = p_transition;
} break;
case TYPE_SCALE_3D: {
ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ ERR_FAIL_COND(st->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, st->scales.size());
st->scales.write[p_key_idx].transition = p_transition;
} break;
case TYPE_BLEND_SHAPE: {
BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ ERR_FAIL_COND(bst->compressed_track >= 0);
ERR_FAIL_INDEX(p_key_idx, bst->blend_shapes.size());
bst->blend_shapes.write[p_key_idx].transition = p_transition;
} break;
@@ -2334,26 +2704,50 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- _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);
+ 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);
- _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 (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);
- _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 (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);
- _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);
+ 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: {
@@ -2408,22 +2802,38 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
switch (t->type) {
case TYPE_POSITION_3D: {
const PositionTrack *tt = static_cast<const PositionTrack *>(t);
- _track_get_key_indices_in_range(tt->positions, from_time, to_time, p_indices);
+ if (tt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(tt->compressed_track, from_time, to_time - from_time, p_indices);
+ } 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);
- _track_get_key_indices_in_range(rt->rotations, from_time, to_time, p_indices);
+ if (rt->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(rt->compressed_track, from_time, to_time - from_time, p_indices);
+ } 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);
- _track_get_key_indices_in_range(st->scales, from_time, to_time, p_indices);
+ if (st->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<3>(st->compressed_track, from_time, to_time - from_time, p_indices);
+ } 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);
- _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
+ if (bst->compressed_track >= 0) {
+ _get_compressed_key_indices_in_range<1>(bst->compressed_track, from_time, to_time - from_time, p_indices);
+ } else {
+ _track_get_key_indices_in_range(bst->blend_shapes, from_time, to_time, p_indices);
+ }
} break;
case TYPE_VALUE: {
@@ -3027,7 +3437,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type);
ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path);
ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path);
- ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track);
+ ClassDB::bind_method(D_METHOD("find_track", "path", "type"), &Animation::find_track);
ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up);
ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down);
@@ -3064,6 +3474,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("track_set_interpolation_loop_wrap", "track_idx", "interpolation"), &Animation::track_set_interpolation_loop_wrap);
ClassDB::bind_method(D_METHOD("track_get_interpolation_loop_wrap", "track_idx"), &Animation::track_get_interpolation_loop_wrap);
+ ClassDB::bind_method(D_METHOD("track_is_compressed", "track_idx"), &Animation::track_is_compressed);
+
ClassDB::bind_method(D_METHOD("value_track_set_update_mode", "track_idx", "mode"), &Animation::value_track_set_update_mode);
ClassDB::bind_method(D_METHOD("value_track_get_update_mode", "track_idx"), &Animation::value_track_get_update_mode);
@@ -3110,6 +3522,8 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
+ 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::FLOAT, "step", PROPERTY_HINT_RANGE, "0,4096,0.001"), "set_step", "get_step");
@@ -3143,6 +3557,10 @@ void Animation::clear() {
tracks.clear();
loop = false;
length = 1;
+ compression.enabled = false;
+ compression.bounds.clear();
+ compression.pages.clear();
+ compression.fps = 120;
emit_changed();
emit_signal(SceneStringNames::get_singleton()->tracks_changed);
}
@@ -3447,6 +3865,9 @@ void Animation::_blend_shape_track_optimize(int p_idx, real_t p_allowed_linear_e
void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_err, real_t p_max_optimizable_angle) {
for (int i = 0; i < tracks.size(); i++) {
+ if (track_is_compressed(i)) {
+ continue; //not possible to optimize compressed track
+ }
if (tracks[i]->type == TYPE_POSITION_3D) {
_position_track_optimize(i, p_allowed_linear_err, p_allowed_angular_err);
} else if (tracks[i]->type == TYPE_ROTATION_3D) {
@@ -3459,6 +3880,1154 @@ void Animation::optimize(real_t p_allowed_linear_err, real_t p_allowed_angular_e
}
}
+#define print_animc(m_str)
+//#define print_animc(m_str) print_line(m_str);
+
+struct AnimationCompressionDataState {
+ enum {
+ MIN_OPTIMIZE_PACKETS = 5,
+ MAX_PACKETS = 16
+ };
+
+ uint32_t components = 3;
+ LocalVector<uint8_t> data; //commited packets
+ struct PacketData {
+ int32_t data[3] = { 0, 0, 0 };
+ uint32_t frame = 0;
+ };
+
+ float split_tolerance = 1.5;
+
+ LocalVector<PacketData> temp_packets;
+
+ //used for rollback if the new frame does not fit
+ int32_t validated_packet_count = -1;
+
+ static int32_t _compute_delta16_signed(int32_t p_from, int32_t p_to) {
+ int32_t delta = p_to - p_from;
+ if (delta > 32767) {
+ return delta - 65536; // use wrap around
+ } else if (delta < -32768) {
+ return 65536 + delta; // use wrap around
+ }
+ return delta;
+ }
+
+ static uint32_t _compute_shift_bits_signed(int32_t p_delta) {
+ if (p_delta == 0) {
+ return 0;
+ } else if (p_delta < 0) {
+ p_delta = ABS(p_delta) - 1;
+ if (p_delta == 0) {
+ return 1;
+ }
+ }
+ return nearest_shift(p_delta);
+ }
+
+ void _compute_max_shifts(uint32_t p_from, uint32_t p_to, uint32_t *max_shifts, uint32_t &max_frame_delta_shift) const {
+ for (uint32_t j = 0; j < components; j++) {
+ max_shifts[j] = 0;
+ }
+ max_frame_delta_shift = 0;
+
+ for (uint32_t i = p_from + 1; i <= p_to; i++) {
+ int32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame;
+ max_frame_delta_shift = MAX(max_frame_delta_shift, nearest_shift(frame_delta));
+ for (uint32_t j = 0; j < components; j++) {
+ int32_t diff = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]);
+ uint32_t shift = _compute_shift_bits_signed(diff);
+ max_shifts[j] = MAX(shift, max_shifts[j]);
+ }
+ }
+ }
+
+ bool insert_key(uint32_t p_frame, const Vector3i &p_key) {
+ if (temp_packets.size() == MAX_PACKETS) {
+ commit_temp_packets();
+ }
+ PacketData packet;
+ packet.frame = p_frame;
+ for (int i = 0; i < 3; i++) {
+ ERR_FAIL_COND_V(p_key[i] > 65535, false); // Sanity check
+ packet.data[i] = p_key[i];
+ }
+
+ temp_packets.push_back(packet);
+
+ if (temp_packets.size() >= MIN_OPTIMIZE_PACKETS) {
+ uint32_t max_shifts[3] = { 0, 0, 0 }; // Base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
+ // Compute the average shift before the packet was added
+ _compute_max_shifts(0, temp_packets.size() - 2, max_shifts, max_frame_delta_shift);
+
+ float prev_packet_size_avg = 0;
+ prev_packet_size_avg = float(1 << max_frame_delta_shift);
+ for (uint32_t i = 0; i < components; i++) {
+ prev_packet_size_avg += float(1 << max_shifts[i]);
+ }
+ prev_packet_size_avg /= float(1 + components);
+
+ _compute_max_shifts(temp_packets.size() - 2, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+
+ float new_packet_size_avg = 0;
+ new_packet_size_avg = float(1 << max_frame_delta_shift);
+ for (uint32_t i = 0; i < components; i++) {
+ new_packet_size_avg += float(1 << max_shifts[i]);
+ }
+ new_packet_size_avg /= float(1 + components);
+
+ print_animc("packet count: " + rtos(temp_packets.size() - 1) + " size avg " + rtos(prev_packet_size_avg) + " new avg " + rtos(new_packet_size_avg));
+ float ratio = (prev_packet_size_avg < new_packet_size_avg) ? (new_packet_size_avg / prev_packet_size_avg) : (prev_packet_size_avg / new_packet_size_avg);
+
+ if (ratio > split_tolerance) {
+ print_animc("split!");
+ temp_packets.resize(temp_packets.size() - 1);
+ commit_temp_packets();
+ temp_packets.push_back(packet);
+ }
+ }
+
+ return temp_packets.size() == 1; // First key
+ }
+
+ uint32_t get_temp_packet_size() const {
+ if (temp_packets.size() == 0) {
+ return 0;
+ } else if (temp_packets.size() == 1) {
+ return components == 1 ? 4 : 8; // 1 component packet is 16 bits and 16 bits unused. 3 component packets is 48 bits and 16 bits unused
+ }
+ uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
+
+ _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+
+ uint32_t size_bits = 16; //base value (all 4 bits of shift sizes for x,y,z,time)
+ size_bits += max_frame_delta_shift * (temp_packets.size() - 1); //times
+ for (uint32_t j = 0; j < components; j++) {
+ size_bits += 16; //base value
+ uint32_t shift = max_shifts[j];
+ if (shift > 0) {
+ shift += 1; //if not zero, add sign bit
+ }
+ size_bits += shift * (temp_packets.size() - 1);
+ }
+ if (size_bits % 8 != 0) { //wrap to 8 bits
+ size_bits += 8 - (size_bits % 8);
+ }
+ uint32_t size_bytes = size_bits / 8; //wrap to words
+ if (size_bytes % 4 != 0) {
+ size_bytes += 4 - (size_bytes % 4);
+ }
+ return size_bytes;
+ }
+
+ static void _push_bits(LocalVector<uint8_t> &data, uint32_t &r_buffer, uint32_t &r_bits_used, uint32_t p_value, uint32_t p_bits) {
+ r_buffer |= p_value << r_bits_used;
+ r_bits_used += p_bits;
+ while (r_bits_used >= 8) {
+ uint8_t byte = r_buffer & 0xFF;
+ data.push_back(byte);
+ r_buffer >>= 8;
+ r_bits_used -= 8;
+ }
+ }
+
+ void commit_temp_packets() {
+ if (temp_packets.size() == 0) {
+ return; //nohing to do
+ }
+#define DEBUG_PACKET_PUSH
+#ifdef DEBUG_PACKET_PUSH
+#ifndef _MSC_VER
+#warning Debugging packet push, disable this code in production to gain a bit more import performance.
+#endif
+ uint32_t debug_packet_push = get_temp_packet_size();
+ uint32_t debug_data_size = data.size();
+#endif
+ // Store header
+
+ uint8_t header[8];
+ uint32_t header_bytes = 0;
+ for (uint32_t i = 0; i < components; i++) {
+ encode_uint16(temp_packets[0].data[i], &header[header_bytes]);
+ header_bytes += 2;
+ }
+
+ uint32_t max_shifts[3] = { 0, 0, 0 }; //base sizes, 16 bit
+ uint32_t max_frame_delta_shift = 0;
+
+ if (temp_packets.size() > 1) {
+ _compute_max_shifts(0, temp_packets.size() - 1, max_shifts, max_frame_delta_shift);
+ uint16_t shift_header = (max_frame_delta_shift - 1) << 12;
+ for (uint32_t i = 0; i < components; i++) {
+ shift_header |= max_shifts[i] << (4 * i);
+ }
+
+ encode_uint16(shift_header, &header[header_bytes]);
+ header_bytes += 2;
+ }
+
+ while (header_bytes % 4 != 0) {
+ header[header_bytes++] = 0;
+ }
+
+ for (uint32_t i = 0; i < header_bytes; i++) {
+ data.push_back(header[i]);
+ }
+
+ if (temp_packets.size() == 1) {
+ temp_packets.clear();
+ validated_packet_count = 0;
+ return; //only header stored, nothing else to do
+ }
+
+ uint32_t bit_buffer = 0;
+ uint32_t bits_used = 0;
+
+ for (uint32_t i = 1; i < temp_packets.size(); i++) {
+ uint32_t frame_delta = temp_packets[i].frame - temp_packets[i - 1].frame;
+ _push_bits(data, bit_buffer, bits_used, frame_delta, max_frame_delta_shift);
+
+ for (uint32_t j = 0; j < components; j++) {
+ if (max_shifts[j] == 0) {
+ continue; // Zero delta, do not store
+ }
+ int32_t delta = _compute_delta16_signed(temp_packets[i - 1].data[j], temp_packets[i].data[j]);
+
+ ERR_FAIL_COND(delta < -32768 || delta > 32767); //sanity check
+
+ uint16_t deltau;
+ if (delta < 0) {
+ deltau = (ABS(delta) - 1) | (1 << max_shifts[j]);
+ } else {
+ deltau = delta;
+ }
+ _push_bits(data, bit_buffer, bits_used, deltau, max_shifts[j] + 1); // Include sign bit
+ }
+ }
+ if (bits_used != 0) {
+ ERR_FAIL_COND(bit_buffer > 0xFF); // Sanity check
+ data.push_back(bit_buffer);
+ }
+
+ while (data.size() % 4 != 0) {
+ data.push_back(0); //pad to align with 4
+ }
+
+ temp_packets.clear();
+ validated_packet_count = 0;
+
+#ifdef DEBUG_PACKET_PUSH
+ ERR_FAIL_COND((data.size() - debug_data_size) != debug_packet_push);
+#endif
+ }
+};
+
+struct AnimationCompressionTimeState {
+ struct Packet {
+ uint32_t frame;
+ uint32_t offset;
+ uint32_t count;
+ };
+
+ LocalVector<Packet> packets;
+ //used for rollback
+ int32_t key_index = 0;
+ int32_t validated_packet_count = 0;
+ int32_t validated_key_index = -1;
+ bool needs_start_frame = false;
+};
+
+Vector3i Animation::_compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key, float p_time) {
+ Vector3i values;
+ TrackType tt = track_get_type(p_track);
+ switch (tt) {
+ case TYPE_POSITION_3D: {
+ Vector3 pos;
+ if (p_key >= 0) {
+ position_track_get_key(p_track, p_key, &pos);
+ } else {
+ position_track_interpolate(p_track, p_time, &pos);
+ }
+ pos = (pos - p_bounds.position) / p_bounds.size;
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(pos[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_ROTATION_3D: {
+ Quaternion rot;
+ if (p_key >= 0) {
+ rotation_track_get_key(p_track, p_key, &rot);
+ } else {
+ rotation_track_interpolate(p_track, p_time, &rot);
+ }
+ Vector3 axis = rot.get_axis();
+ float angle = rot.get_angle();
+ angle = Math::fposmod(double(angle), double(Math_PI * 2.0));
+ Vector2 oct = axis.octahedron_encode();
+ Vector3 rot_norm(oct.x, oct.y, angle / (Math_PI * 2.0)); // high resolution rotation in 0-1 angle.
+
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(rot_norm[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_SCALE_3D: {
+ Vector3 scale;
+ if (p_key >= 0) {
+ scale_track_get_key(p_track, p_key, &scale);
+ } else {
+ scale_track_interpolate(p_track, p_time, &scale);
+ }
+ scale = (scale - p_bounds.position) / p_bounds.size;
+ for (int j = 0; j < 3; j++) {
+ values[j] = CLAMP(int32_t(scale[j] * 65535.0), 0, 65535);
+ }
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ float blend;
+ if (p_key >= 0) {
+ blend_shape_track_get_key(p_track, p_key, &blend);
+ } else {
+ blend_shape_track_interpolate(p_track, p_time, &blend);
+ }
+
+ blend = (blend / float(Compression::BLEND_SHAPE_RANGE)) * 0.5 + 0.5;
+ values[0] = CLAMP(int32_t(blend * 65535.0), 0, 65535);
+ } break;
+ default: {
+ ERR_FAIL_V(Vector3i()); //sanity check
+ } break;
+ }
+
+ return values;
+}
+
+struct AnimationCompressionBufferBitsRead {
+ uint32_t buffer = 0;
+ uint32_t used = 0;
+ const uint8_t *src_data = nullptr;
+
+ _FORCE_INLINE_ uint32_t read(uint32_t p_bits) {
+ uint32_t output = 0;
+ uint32_t written = 0;
+ while (p_bits > 0) {
+ if (used == 0) {
+ used = 8;
+ buffer = *src_data;
+ src_data++;
+ }
+ uint32_t to_write = MIN(used, p_bits);
+ output |= (buffer & ((1 << to_write) - 1)) << written;
+ buffer >>= to_write;
+ used -= to_write;
+ p_bits -= to_write;
+ written += to_write;
+ }
+ return output;
+ }
+};
+
+void Animation::compress(uint32_t p_page_size, uint32_t p_fps, float p_split_tolerance) {
+ ERR_FAIL_COND_MSG(compression.enabled, "This animation is already compressed");
+
+ p_split_tolerance = CLAMP(p_split_tolerance, 1.1, 8.0);
+ compression.pages.clear();
+
+ uint32_t base_page_size = 0; // Before compressing pages, compute how large the "end page" datablock is.
+ LocalVector<uint32_t> tracks_to_compress;
+ LocalVector<AABB> track_bounds;
+ const uint32_t time_packet_size = 4;
+
+ const uint32_t track_header_size = 4 + 4 + 4; // pointer to time (4 bytes), amount of time keys (4 bytes) pointer to track data (4 bytes)
+
+ for (int i = 0; i < get_track_count(); i++) {
+ TrackType type = track_get_type(i);
+ if (type != TYPE_POSITION_3D && type != TYPE_ROTATION_3D && type != TYPE_SCALE_3D && type != TYPE_BLEND_SHAPE) {
+ continue;
+ }
+ if (track_get_key_count(i) == 0) {
+ continue; //do not compress, no keys
+ }
+ base_page_size += track_header_size; //pointer to beginning of each track timeline and amount of time keys
+ base_page_size += time_packet_size; //for end of track time marker
+ base_page_size += (type == TYPE_BLEND_SHAPE) ? 4 : 8; // at least the end of track packet (at much 8 bytes). This could be less, but have to be pessimistic.
+ tracks_to_compress.push_back(i);
+
+ AABB bounds;
+
+ if (type == TYPE_POSITION_3D) {
+ AABB aabb;
+ int kcount = track_get_key_count(i);
+ for (int j = 0; j < kcount; j++) {
+ Vector3 pos;
+ position_track_get_key(i, j, &pos);
+ if (j == 0) {
+ aabb.position = pos;
+ } else {
+ aabb.expand_to(pos);
+ }
+ }
+ for (int j = 0; j < 3; j++) {
+ //cant have zero
+ if (aabb.size[j] < CMP_EPSILON) {
+ aabb.size[j] = CMP_EPSILON;
+ }
+ }
+ bounds = aabb;
+ }
+ if (type == TYPE_SCALE_3D) {
+ AABB aabb;
+ int kcount = track_get_key_count(i);
+ for (int j = 0; j < kcount; j++) {
+ Vector3 scale;
+ scale_track_get_key(i, j, &scale);
+ if (j == 0) {
+ aabb.position = scale;
+ } else {
+ aabb.expand_to(scale);
+ }
+ }
+ for (int j = 0; j < 3; j++) {
+ //cant have zero
+ if (aabb.size[j] < CMP_EPSILON) {
+ aabb.size[j] = CMP_EPSILON;
+ }
+ }
+ bounds = aabb;
+ }
+
+ track_bounds.push_back(bounds);
+ }
+
+ if (tracks_to_compress.size() == 0) {
+ return; //nothing to compress
+ }
+
+ print_animc("Anim Compression:");
+ print_animc("-----------------");
+ print_animc("Tracks to compress: " + itos(tracks_to_compress.size()));
+
+ uint32_t current_frame = 0;
+ uint32_t base_page_frame = 0;
+ double frame_len = 1.0 / double(p_fps);
+ const uint32_t max_frames_per_page = 65536;
+
+ print_animc("Frame Len: " + rtos(frame_len));
+
+ LocalVector<AnimationCompressionDataState> data_tracks;
+ LocalVector<AnimationCompressionTimeState> time_tracks;
+
+ data_tracks.resize(tracks_to_compress.size());
+ time_tracks.resize(tracks_to_compress.size());
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].split_tolerance = p_split_tolerance;
+ if (track_get_type(tracks_to_compress[i]) == TYPE_BLEND_SHAPE) {
+ data_tracks[i].components = 1;
+ } else {
+ data_tracks[i].components = 3;
+ }
+ }
+
+ while (true) {
+ // Begin by finding the keyframe in all tracks with the time closest to the current time
+ const uint32_t FRAME_MAX = 0xFFFFFFFF;
+ const int32_t NO_TRACK_FOUND = -1;
+ uint32_t best_frame = FRAME_MAX;
+ uint32_t best_invalid_frame = FRAME_MAX;
+ int32_t best_frame_track = NO_TRACK_FOUND; // Default is -1, which means all keyframes for this page are exhausted.
+ bool start_frame = false;
+
+ for (uint32_t i = 0; i < tracks_to_compress.size(); i++) {
+ uint32_t uncomp_track = tracks_to_compress[i];
+
+ if (time_tracks[i].key_index == track_get_key_count(uncomp_track)) {
+ if (time_tracks[i].needs_start_frame) {
+ start_frame = true;
+ best_frame = base_page_frame;
+ best_frame_track = i;
+ time_tracks[i].needs_start_frame = false;
+ break;
+ } else {
+ continue; // This track is exhausted (all keys were added already), don't consider.
+ }
+ }
+
+ uint32_t key_frame = double(track_get_key_time(uncomp_track, time_tracks[i].key_index)) / frame_len;
+
+ if (time_tracks[i].needs_start_frame && key_frame > base_page_frame) {
+ start_frame = true;
+ best_frame = base_page_frame;
+ best_frame_track = i;
+ time_tracks[i].needs_start_frame = false;
+ break;
+ }
+
+ ERR_FAIL_COND(key_frame < base_page_frame); // Sanity check, should never happen
+
+ if (key_frame - base_page_frame >= max_frames_per_page) {
+ // Invalid because beyond the max frames allowed per page
+ best_invalid_frame = MIN(best_invalid_frame, key_frame);
+ } else if (key_frame < best_frame) {
+ best_frame = key_frame;
+ best_frame_track = i;
+ }
+ }
+
+ print_animc("*KEY*: Current Frame: " + itos(current_frame) + " Best Frame: " + rtos(best_frame) + " Best Track: " + itos(best_frame_track) + " Start: " + String(start_frame ? "true" : "false"));
+
+ if (!start_frame && best_frame > current_frame) {
+ // Any case where the current frame advanced, either because nothing was found or because something was found greater than the current one.
+ print_animc("\tAdvance Condition.");
+ bool rollback = false;
+
+ // The frame has advanced, time to validate the previous frame
+ uint32_t current_page_size = base_page_size;
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ uint32_t track_size = data_tracks[i].data.size(); // track size
+ track_size += data_tracks[i].get_temp_packet_size(); // Add the temporary data
+ if (track_size > Compression::MAX_DATA_TRACK_SIZE) {
+ rollback = true; //track to large, time track can't point to keys any longer, because key offset is 12 bits
+ break;
+ }
+ current_page_size += track_size;
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ current_page_size += time_tracks[i].packets.size() * 4; // time packet is 32 bits
+ }
+
+ if (!rollback && current_page_size > p_page_size) {
+ rollback = true;
+ }
+
+ print_animc("\tCurrent Page Size: " + itos(current_page_size) + "/" + itos(p_page_size) + " Rollback? " + String(rollback ? "YES!" : "no"));
+
+ if (rollback) {
+ // Not valid any longer, so rollback and commit page
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].temp_packets.resize(data_tracks[i].validated_packet_count);
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ time_tracks[i].key_index = time_tracks[i].validated_key_index; //rollback key
+ time_tracks[i].packets.resize(time_tracks[i].validated_packet_count);
+ }
+
+ } else {
+ // All valid, so save rollback information
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ data_tracks[i].validated_packet_count = data_tracks[i].temp_packets.size();
+ }
+ for (uint32_t i = 0; i < time_tracks.size(); i++) {
+ time_tracks[i].validated_key_index = time_tracks[i].key_index;
+ time_tracks[i].validated_packet_count = time_tracks[i].packets.size();
+ }
+
+ // Accept this frame as the frame being processed (as long as it exists)
+ if (best_frame != FRAME_MAX) {
+ current_frame = best_frame;
+ print_animc("\tValidated, New Current Frame: " + itos(current_frame));
+ }
+ }
+
+ if (rollback || best_frame == FRAME_MAX) {
+ // Commit the page if had to rollback or if no track was found
+ print_animc("\tCommiting page..");
+
+ // The end frame for the page depends entirely on whether its valid or
+ // no more keys were found.
+ // If not valid, then the end frame is the current frame (as this means the current frame is being rolled back
+ // If valid, then the end frame is the next invalid one (in case more frames exist), or the current frame in case no more frames exist.
+ uint32_t page_end_frame = (rollback || best_frame == FRAME_MAX) ? current_frame : best_invalid_frame;
+
+ print_animc("\tEnd Frame: " + itos(page_end_frame) + ", " + rtos(page_end_frame * frame_len) + "s");
+
+ // Add finalizer frames and commit pending tracks
+ uint32_t finalizer_local_frame = page_end_frame - base_page_frame;
+
+ uint32_t total_page_size = 0;
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ if (data_tracks[i].temp_packets.size() == 0 || (data_tracks[i].temp_packets[data_tracks[i].temp_packets.size() - 1].frame) < finalizer_local_frame) {
+ // Add finalizer frame if it makes sense
+ Vector3i values = _compress_key(tracks_to_compress[i], track_bounds[i], -1, page_end_frame * frame_len);
+
+ bool first_key = data_tracks[i].insert_key(finalizer_local_frame, values);
+ if (first_key) {
+ AnimationCompressionTimeState::Packet p;
+ p.count = 1;
+ p.frame = finalizer_local_frame;
+ p.offset = data_tracks[i].data.size();
+ time_tracks[i].packets.push_back(p);
+ } else {
+ ERR_FAIL_COND(time_tracks[i].packets.size() == 0);
+ time_tracks[i].packets[time_tracks[i].packets.size() - 1].count++;
+ }
+ }
+
+ data_tracks[i].commit_temp_packets();
+ total_page_size += data_tracks[i].data.size();
+ total_page_size += time_tracks[i].packets.size() * 4;
+ total_page_size += track_header_size;
+
+ print_animc("\tTrack " + itos(i) + " time packets: " + itos(time_tracks[i].packets.size()) + " Packet data: " + itos(data_tracks[i].data.size()));
+ }
+
+ print_animc("\tTotal page Size: " + itos(total_page_size) + "/" + itos(p_page_size));
+
+ // Create Page
+ Vector<uint8_t> page_data;
+ page_data.resize(total_page_size);
+ {
+ uint8_t *page_ptr = page_data.ptrw();
+ uint32_t base_offset = data_tracks.size() * track_header_size;
+
+ for (uint32_t i = 0; i < data_tracks.size(); i++) {
+ encode_uint32(base_offset, page_ptr + (track_header_size * i + 0));
+ uint16_t *key_time_ptr = (uint16_t *)(page_ptr + base_offset);
+ for (uint32_t j = 0; j < time_tracks[i].packets.size(); j++) {
+ key_time_ptr[j * 2 + 0] = uint16_t(time_tracks[i].packets[j].frame);
+ uint16_t ptr = time_tracks[i].packets[j].offset / 4;
+ ptr |= (time_tracks[i].packets[j].count - 1) << 12;
+ key_time_ptr[j * 2 + 1] = ptr;
+ base_offset += 4;
+ }
+ encode_uint32(time_tracks[i].packets.size(), page_ptr + (track_header_size * i + 4));
+ encode_uint32(base_offset, page_ptr + (track_header_size * i + 8));
+ memcpy(page_ptr + base_offset, data_tracks[i].data.ptr(), data_tracks[i].data.size());
+ base_offset += data_tracks[i].data.size();
+
+ //reset track
+ data_tracks[i].data.clear();
+ data_tracks[i].temp_packets.clear();
+ data_tracks[i].validated_packet_count = -1;
+
+ time_tracks[i].needs_start_frame = true; //Not required the first time, but from now on it is.
+ time_tracks[i].packets.clear();
+ time_tracks[i].validated_key_index = -1;
+ time_tracks[i].validated_packet_count = 0;
+ }
+ }
+
+ Compression::Page page;
+ page.data = page_data;
+ page.time_offset = base_page_frame * frame_len;
+ compression.pages.push_back(page);
+
+ if (!rollback && best_invalid_frame == FRAME_MAX) {
+ break; // No more pages to add.
+ }
+
+ current_frame = page_end_frame;
+ base_page_frame = page_end_frame;
+
+ continue; // Start over
+ }
+ }
+
+ // A key was found for the current frame and all is ok
+
+ uint32_t comp_track = best_frame_track;
+ Vector3i values;
+
+ if (start_frame) {
+ // Interpolate
+ values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], -1, base_page_frame * frame_len);
+ } else {
+ uint32_t key = time_tracks[comp_track].key_index;
+ values = _compress_key(tracks_to_compress[comp_track], track_bounds[comp_track], key);
+ time_tracks[comp_track].key_index++; //goto next key (but could be rolled back if beyond page size).
+ }
+
+ bool first_key = data_tracks[comp_track].insert_key(best_frame - base_page_frame, values);
+ if (first_key) {
+ AnimationCompressionTimeState::Packet p;
+ p.count = 1;
+ p.frame = best_frame - base_page_frame;
+ p.offset = data_tracks[comp_track].data.size();
+ time_tracks[comp_track].packets.push_back(p);
+ } else {
+ ERR_CONTINUE(time_tracks[comp_track].packets.size() == 0);
+ time_tracks[comp_track].packets[time_tracks[comp_track].packets.size() - 1].count++;
+ }
+ }
+
+ compression.bounds = track_bounds;
+ compression.fps = p_fps;
+ compression.enabled = true;
+
+ for (uint32_t i = 0; i < tracks_to_compress.size(); i++) {
+ Track *t = tracks[tracks_to_compress[i]];
+ t->interpolation = INTERPOLATION_LINEAR; //only linear supported
+ switch (t->type) {
+ case TYPE_POSITION_3D: {
+ PositionTrack *tt = static_cast<PositionTrack *>(t);
+ tt->positions.clear();
+ tt->compressed_track = i;
+ } break;
+ case TYPE_ROTATION_3D: {
+ RotationTrack *rt = static_cast<RotationTrack *>(t);
+ rt->rotations.clear();
+ rt->compressed_track = i;
+ } break;
+ case TYPE_SCALE_3D: {
+ ScaleTrack *st = static_cast<ScaleTrack *>(t);
+ st->scales.clear();
+ st->compressed_track = i;
+ print_line("Scale Bounds " + itos(i) + ": " + track_bounds[i]);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ BlendShapeTrack *bst = static_cast<BlendShapeTrack *>(t);
+ bst->blend_shapes.clear();
+ bst->compressed_track = i;
+ } break;
+ default: {
+ }
+ }
+ }
+#if 1
+ uint32_t orig_size = 0;
+ for (int i = 0; i < get_track_count(); i++) {
+ switch (track_get_type(i)) {
+ case TYPE_SCALE_3D:
+ case TYPE_POSITION_3D: {
+ orig_size += sizeof(TKey<Vector3>) * track_get_key_count(i);
+ } break;
+ case TYPE_ROTATION_3D: {
+ orig_size += sizeof(TKey<Quaternion>) * track_get_key_count(i);
+ } break;
+ case TYPE_BLEND_SHAPE: {
+ orig_size += sizeof(TKey<float>) * track_get_key_count(i);
+ } break;
+ default: {
+ }
+ }
+ }
+
+ uint32_t new_size = 0;
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ new_size += compression.pages[i].data.size();
+ }
+
+ print_line("Original size: " + itos(orig_size) + " - Compressed size: " + itos(new_size) + " " + String::num(float(new_size) / float(orig_size) * 100, 2) + "% pages: " + itos(compression.pages.size()));
+#endif
+}
+
+bool Animation::_rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
+
+ if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
+
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_quaternion(current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_quaternion(next);
+ } else {
+ double c = (p_time - time_current) / (time_next - time_current);
+ Quaternion from = _uncompress_quaternion(current);
+ Quaternion to = _uncompress_quaternion(next);
+ r_ret = from.slerp(to, c);
+ }
+
+ return true;
+}
+
+bool Animation::_pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
+
+ if (!_fetch_compressed<3>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
+
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_pos_scale(p_compressed_track, current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_pos_scale(p_compressed_track, next);
+ } else {
+ double c = (p_time - time_current) / (time_next - time_current);
+ Vector3 from = _uncompress_pos_scale(p_compressed_track, current);
+ Vector3 to = _uncompress_pos_scale(p_compressed_track, next);
+ r_ret = from.lerp(to, c);
+ }
+
+ return true;
+}
+bool Animation::_blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const {
+ Vector3i current;
+ Vector3i next;
+ double time_current;
+ double time_next;
+
+ if (!_fetch_compressed<1>(p_compressed_track, p_time, current, time_current, next, time_next)) {
+ return false; //some sort of problem
+ }
+
+ if (time_current >= p_time || time_current == time_next) {
+ r_ret = _uncompress_blend_shape(current);
+ } else if (p_time >= time_next) {
+ r_ret = _uncompress_blend_shape(next);
+ } else {
+ float c = (p_time - time_current) / (time_next - time_current);
+ float from = _uncompress_blend_shape(current);
+ float to = _uncompress_blend_shape(next);
+ r_ret = Math::lerp(from, to, c);
+ }
+
+ return true;
+}
+
+template <uint32_t COMPONENTS>
+bool Animation::_fetch_compressed(uint32_t p_compressed_track, double p_time, Vector3i &r_current_value, double &r_current_time, Vector3i &r_next_value, double &r_next_time, uint32_t *key_index) const {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false);
+ p_time = CLAMP(p_time, 0, length);
+ if (key_index) {
+ *key_index = 0;
+ }
+
+ double frame_to_sec = 1.0 / double(compression.fps);
+
+ int32_t page_index = -1;
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ if (compression.pages[i].time_offset > p_time) {
+ break;
+ }
+ page_index = i;
+ }
+
+ ERR_FAIL_COND_V(page_index == -1, false); //should not happen
+
+ double page_base_time = compression.pages[page_index].time_offset;
+ const uint8_t *page_data = compression.pages[page_index].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ int32_t packet_idx = 0;
+ double packet_time = double(time_keys[0]) * frame_to_sec + page_base_time;
+ uint32_t base_frame = time_keys[0];
+
+ for (uint32_t i = 1; i < time_key_count; i++) {
+ uint32_t f = time_keys[i * 2 + 0];
+ double frame_time = double(f) * frame_to_sec + page_base_time;
+
+ if (frame_time > p_time) {
+ break;
+ }
+
+ if (key_index) {
+ (*key_index) += (time_keys[(i - 1) * 2 + 1] >> 12) + 1;
+ }
+
+ packet_idx = i;
+ packet_time = frame_time;
+ base_frame = f;
+ }
+
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ uint16_t time_key_data = time_keys[packet_idx * 2 + 1];
+ uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits
+ uint32_t data_count = (time_key_data >> 12) + 1;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ uint16_t decode[COMPONENTS];
+ uint16_t decode_next[COMPONENTS];
+
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ decode[i] = data_key[i];
+ decode_next[i] = data_key[i];
+ }
+
+ double next_time = packet_time;
+
+ if (p_time > packet_time) { // If its equal or less, then don't bother
+ if (data_count > 1) {
+ //decode forward
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ bit_width[i] = (data_key[COMPONENTS] >> (i * 4)) & 0xF;
+ }
+
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
+
+ AnimationCompressionBufferBitsRead buffer;
+
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (uint32_t i = 1; i < data_count; i++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ base_frame += frame_delta;
+
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ if (bit_width[j] == 0) {
+ continue; // do none
+ }
+ uint32_t valueu = buffer.read(bit_width[j] + 1);
+ bool sign = valueu & (1 << bit_width[j]);
+ int16_t value = valueu & ((1 << bit_width[j]) - 1);
+ if (sign) {
+ value = -value - 1;
+ }
+
+ decode_next[j] += value;
+ }
+
+ next_time = double(base_frame) * frame_to_sec + page_base_time;
+ if (p_time < next_time) {
+ break;
+ }
+
+ packet_time = next_time;
+
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ decode[j] = decode_next[j];
+ }
+
+ if (key_index) {
+ (*key_index)++;
+ }
+ }
+ }
+
+ if (p_time > next_time) { // > instead of >= because if its equal, then it will be properly interpolated anyway
+ // So, the last frame found still has a time that is less than the required frame,
+ // will have to interpolate with the first frame of the next timekey.
+
+ if ((uint32_t)packet_idx < time_key_count - 1) { // Sanity check but should not matter much, otherwise current next packet is last packet
+
+ uint16_t time_key_data_next = time_keys[(packet_idx + 1) * 2 + 1];
+ uint32_t data_offset_next = (time_key_data_next & 0xFFF) * 4; // Lower 12 bits
+
+ const uint16_t *data_key_next = (const uint16_t *)(data_keys_base + data_offset_next);
+ base_frame = time_keys[(packet_idx + 1) * 2 + 0];
+ next_time = double(base_frame) * frame_to_sec + page_base_time;
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ decode_next[i] = data_key_next[i];
+ }
+ }
+ }
+ }
+
+ r_current_time = packet_time;
+ r_next_time = next_time;
+
+ for (uint32_t i = 0; i < COMPONENTS; i++) {
+ r_current_value[i] = decode[i];
+ r_next_value[i] = decode_next[i];
+ }
+
+ return true;
+}
+
+template <uint32_t COMPONENTS>
+void Animation::_get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const {
+ ERR_FAIL_COND(!compression.enabled);
+ ERR_FAIL_UNSIGNED_INDEX(p_compressed_track, compression.bounds.size());
+
+ double frame_to_sec = 1.0 / double(compression.fps);
+ uint32_t key_index = 0;
+
+ for (uint32_t p = 0; p < compression.pages.size(); p++) {
+ if (compression.pages[p].time_offset >= p_time + p_delta) {
+ // Page beyond range
+ return;
+ }
+
+ // Page within range
+
+ uint32_t page_index = p;
+
+ double page_base_time = compression.pages[page_index].time_offset;
+ const uint8_t *page_data = compression.pages[page_index].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ for (uint32_t i = 0; i < time_key_count; i++) {
+ uint32_t f = time_keys[i * 2 + 0];
+ double frame_time = f * frame_to_sec + page_base_time;
+ if (frame_time >= p_time + p_delta) {
+ return;
+ } else if (frame_time >= p_time) {
+ r_indices->push_back(key_index);
+ }
+
+ key_index++;
+
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ uint16_t time_key_data = time_keys[i * 2 + 1];
+ uint32_t data_offset = (time_key_data & 0xFFF) * 4; // lower 12 bits
+ uint32_t data_count = (time_key_data >> 12) + 1;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ if (data_count > 1) {
+ //decode forward
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t j = 0; j < COMPONENTS; j++) {
+ bit_width[j] = (data_key[COMPONENTS] >> (j * 4)) & 0xF;
+ }
+
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
+
+ AnimationCompressionBufferBitsRead buffer;
+
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (uint32_t j = 1; j < data_count; j++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ f += frame_delta;
+
+ frame_time = f * frame_to_sec + page_base_time;
+ if (frame_time >= p_time + p_delta) {
+ return;
+ } else if (frame_time >= p_time) {
+ r_indices->push_back(key_index);
+ }
+
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ if (bit_width[k] == 0) {
+ continue; // do none
+ }
+ buffer.read(bit_width[k] + 1); // skip
+ }
+
+ key_index++;
+ }
+ }
+ }
+ }
+}
+
+int Animation::_get_compressed_key_count(uint32_t p_compressed_track) const {
+ ERR_FAIL_COND_V(!compression.enabled, -1);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), -1);
+
+ int key_count = 0;
+
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ const uint8_t *page_data = compression.pages[i].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+
+ for (uint32_t j = 0; j < time_key_count; j++) {
+ key_count += (time_keys[j * 2 + 1] >> 12) + 1;
+ }
+ }
+
+ return key_count;
+}
+
+Quaternion Animation::_uncompress_quaternion(const Vector3i &p_value) const {
+ Vector3 axis = Vector3::octahedron_decode(Vector2(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0));
+ float angle = (float(p_value.z) / 65535.0) * 2.0 * Math_PI;
+ return Quaternion(axis, angle);
+}
+Vector3 Animation::_uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const {
+ Vector3 pos_norm(float(p_value.x) / 65535.0, float(p_value.y) / 65535.0, float(p_value.z) / 65535.0);
+ return compression.bounds[p_compressed_track].position + pos_norm * compression.bounds[p_compressed_track].size;
+}
+float Animation::_uncompress_blend_shape(const Vector3i &p_value) const {
+ float bsn = float(p_value.x) / 65535.0;
+ return (bsn * 2.0 - 1.0) * float(Compression::BLEND_SHAPE_RANGE);
+}
+
+template <uint32_t COMPONENTS>
+bool Animation::_fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const {
+ ERR_FAIL_COND_V(!compression.enabled, false);
+ ERR_FAIL_UNSIGNED_INDEX_V(p_compressed_track, compression.bounds.size(), false);
+
+ for (uint32_t i = 0; i < compression.pages.size(); i++) {
+ const uint8_t *page_data = compression.pages[i].data.ptr();
+#ifndef _MSC_VER
+#warning Little endian assumed. No major big endian hardware exists any longer, but in case it does it will need to be supported
+#endif
+ const uint32_t *indices = (const uint32_t *)page_data;
+ const uint16_t *time_keys = (const uint16_t *)&page_data[indices[p_compressed_track * 3 + 0]];
+ uint32_t time_key_count = indices[p_compressed_track * 3 + 1];
+ const uint8_t *data_keys_base = (const uint8_t *)&page_data[indices[p_compressed_track * 3 + 2]];
+
+ for (uint32_t j = 0; j < time_key_count; j++) {
+ uint32_t subkeys = (time_keys[j * 2 + 1] >> 12) + 1;
+ if ((uint32_t)p_index < subkeys) {
+ uint16_t data_offset = (time_keys[j * 2 + 1] & 0xFFF) * 4;
+
+ const uint16_t *data_key = (const uint16_t *)(data_keys_base + data_offset);
+
+ uint16_t frame = time_keys[j * 2 + 0];
+ uint16_t decode[COMPONENTS];
+
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ decode[k] = data_key[k];
+ }
+
+ if (p_index > 0) {
+ uint32_t bit_width[COMPONENTS];
+ for (uint32_t k = 0; k < COMPONENTS; k++) {
+ bit_width[k] = (data_key[COMPONENTS] >> (k * 4)) & 0xF;
+ }
+ uint32_t frame_bit_width = (data_key[COMPONENTS] >> 12) + 1;
+
+ AnimationCompressionBufferBitsRead buffer;
+ buffer.src_data = (const uint8_t *)&data_key[COMPONENTS + 1];
+
+ for (int k = 0; k < p_index; k++) {
+ uint32_t frame_delta = buffer.read(frame_bit_width);
+ frame += frame_delta;
+ for (uint32_t l = 0; l < COMPONENTS; l++) {
+ if (bit_width[l] == 0) {
+ continue; // do none
+ }
+ uint32_t valueu = buffer.read(bit_width[l] + 1);
+ bool sign = valueu & (1 << bit_width[l]);
+ int16_t value = valueu & ((1 << bit_width[l]) - 1);
+ if (sign) {
+ value = -value - 1;
+ }
+
+ decode[l] += value;
+ }
+ }
+ }
+
+ r_time = compression.pages[i].time_offset + double(frame) / double(compression.fps);
+ for (uint32_t l = 0; l < COMPONENTS; l++) {
+ r_value[l] = decode[l];
+ }
+
+ return true;
+
+ } else {
+ p_index -= subkeys;
+ }
+ }
+ }
+
+ return false;
+}
+
Animation::Animation() {}
Animation::~Animation() {
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 4ee0741d87..ee07fb19d3 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -32,6 +32,7 @@
#define ANIMATION_H
#include "core/io/resource.h"
+#include "core/templates/local_vector.h"
#define ANIM_MIN_LENGTH 0.001
@@ -98,7 +99,7 @@ private:
struct PositionTrack : public Track {
Vector<TKey<Vector3>> positions;
-
+ int32_t compressed_track = -1;
PositionTrack() { type = TYPE_POSITION_3D; }
};
@@ -106,7 +107,7 @@ private:
struct RotationTrack : public Track {
Vector<TKey<Quaternion>> rotations;
-
+ int32_t compressed_track = -1;
RotationTrack() { type = TYPE_ROTATION_3D; }
};
@@ -114,6 +115,7 @@ private:
struct ScaleTrack : public Track {
Vector<TKey<Vector3>> scales;
+ int32_t compressed_track = -1;
ScaleTrack() { type = TYPE_SCALE_3D; }
};
@@ -121,6 +123,7 @@ private:
struct BlendShapeTrack : public Track {
Vector<TKey<float>> blend_shapes;
+ int32_t compressed_track = -1;
BlendShapeTrack() { type = TYPE_BLEND_SHAPE; }
};
@@ -230,6 +233,89 @@ private:
real_t step = 0.1;
bool loop = false;
+ /* Animation compression page format (version 1):
+ *
+ * Animation uses bitwidth based compression separated into small pages. The intention is that pages fit easily in the cache, so decoding is cache efficient.
+ * The page-based nature also makes future animation streaming from disk possible.
+ *
+ * Actual format:
+ *
+ * num_compressed_tracks = bounds.size()
+ * header : (x num_compressed_tracks)
+ * -------
+ * timeline_keys_offset : uint32_t - offset to time keys
+ * timeline_size : uint32_t - amount of time keys
+ * data_keys_offset : uint32_t offset to key data
+ *
+ * time key (uint32_t):
+ * ------------------
+ * frame : bits 0-15 - time offset of key, computed as: page.time_offset + frame * (1.0/fps)
+ * data_key_offset : bits 16-27 - offset to key data, computed as: data_keys_offset * 4 + data_key_offset
+ * data_key_count : bits 28-31 - amount of data keys pointed to, computed as: data_key_count+1 (max 16)
+ *
+ * data key:
+ * ---------
+ * X / Blend Shape : uint16_t - X coordinate of XYZ vector key, or Blend Shape value. If Blend shape, Y and Z are not present and can be ignored.
+ * Y : uint16_t
+ * Z : uint16_t
+ * If data_key_count+1 > 1 (if more than 1 key is stored):
+ * data_bitwidth : uint16_t - This is only present if data_key_count > 1. Contains delta bitwidth information.
+ * X / Blend Shape delta bitwidth: bits 0-3 -
+ * if 0, nothing is present for X (use the first key-value for subsequent keys),
+ * else assume the number of bits present for each element (+ 1 for sign). Assumed always 16 bits, delta max signed 15 bits, with underflow and overflow supported.
+ * Y delta bitwidth : bits 4-7
+ * Z delta bitwidth : bits 8-11
+ * FRAME delta bitwidth : 12-15 bits - always present (obviously), actual bitwidth is FRAME+1
+ * Data key is 4 bytes long for Blend Shapes, 8 bytes long for pos/rot/scale.
+ *
+ * delta keys:
+ * -----------
+ * Compressed format is packed in the following format after the data key, containing delta keys one after the next in a tightly bit packed fashion.
+ * FRAME bits -> X / Blend Shape Bits (if bitwidth > 0) -> Y Bits (if not Blend Shape and Y Bitwidth > 0) -> Z Bits (if not Blend Shape and Z Bitwidth > 0)
+ *
+ * data key format:
+ * ----------------
+ * Decoding keys means starting from the base key and going key by key applying deltas until the proper position is reached needed for interpolation.
+ * Resulting values are uint32_t
+ * data for X / Blend Shape, Y and Z must be normalized first: unorm = float(data) / 65535.0
+ * **Blend Shape**: (unorm * 2.0 - 1.0) * Compression::BLEND_SHAPE_RANGE
+ * **Pos/Scale**: unorm_vec3 * bounds[track].size + bounds[track].position
+ * **Rotation**: Quaternion(Vector3::octahedron_decode(unorm_vec3.xy),unorm_vec3.z * Math_PI * 2.0)
+ * **Frame**: page.time_offset + frame * (1.0/fps)
+ */
+
+ struct Compression {
+ enum {
+ MAX_DATA_TRACK_SIZE = 16384,
+ BLEND_SHAPE_RANGE = 8, // - 8.0 to 8.0
+ FORMAT_VERSION = 1
+ };
+ struct Page {
+ Vector<uint8_t> data;
+ double time_offset;
+ };
+
+ uint32_t fps = 120;
+ LocalVector<Page> pages;
+ LocalVector<AABB> bounds; //used by position and scale tracks (which contain index to track and index to bounds).
+ bool enabled = false;
+ } compression;
+
+ Vector3i _compress_key(uint32_t p_track, const AABB &p_bounds, int32_t p_key = -1, float p_time = 0.0);
+ bool _rotation_interpolate_compressed(uint32_t p_compressed_track, double p_time, Quaternion &r_ret) const;
+ bool _pos_scale_interpolate_compressed(uint32_t p_compressed_track, double p_time, Vector3 &r_ret) const;
+ bool _blend_shape_interpolate_compressed(uint32_t p_compressed_track, double p_time, float &r_ret) const;
+ template <uint32_t COMPONENTS>
+ bool _fetch_compressed(uint32_t p_compressed_track, double p_time, Vector3i &r_current_value, double &r_current_time, Vector3i &r_next_value, double &r_next_time, uint32_t *key_index = nullptr) const;
+ template <uint32_t COMPONENTS>
+ bool _fetch_compressed_by_index(uint32_t p_compressed_track, int p_index, Vector3i &r_value, double &r_time) const;
+ int _get_compressed_key_count(uint32_t p_compressed_track) const;
+ template <uint32_t COMPONENTS>
+ void _get_compressed_key_indices_in_range(uint32_t p_compressed_track, double p_time, double p_delta, List<int> *r_indices) const;
+ _FORCE_INLINE_ Quaternion _uncompress_quaternion(const Vector3i &p_value) const;
+ _FORCE_INLINE_ Vector3 _uncompress_pos_scale(uint32_t p_compressed_track, const Vector3i &p_value) const;
+ _FORCE_INLINE_ float _uncompress_blend_shape(const Vector3i &p_value) const;
+
// bind helpers
private:
Vector<int> _value_track_get_key_indices(int p_track, double p_time, double p_delta) const {
@@ -281,8 +367,7 @@ public:
void track_set_path(int p_track, const NodePath &p_path);
NodePath track_get_path(int p_track) const;
- int find_track(const NodePath &p_path) const;
- // transform
+ int find_track(const NodePath &p_path, const TrackType p_type) const;
void track_move_up(int p_track);
void track_move_down(int p_track);
@@ -306,6 +391,7 @@ public:
Variant track_get_key_value(int p_track, int p_key_idx) const;
double track_get_key_time(int p_track, int p_key_idx) const;
real_t track_get_key_transition(int p_track, int p_key_idx) const;
+ bool track_is_compressed(int p_track) const;
int position_track_insert_key(int p_track, double p_time, const Vector3 &p_position);
Error position_track_get_key(int p_track, int p_key, Vector3 *r_position) const;
@@ -376,6 +462,7 @@ public:
void clear();
void optimize(real_t p_allowed_linear_err = 0.05, real_t p_allowed_angular_err = 0.01, real_t p_max_optimizable_angle = Math_PI * 0.125);
+ void compress(uint32_t p_page_size = 8192, uint32_t p_fps = 120, float p_split_tolerance = 4.0); // 4.0 seems to be the split tolerance sweet spot from many tests
Animation();
~Animation();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 9fdfd493c1..f21a070133 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -147,6 +147,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
Color control_font_lower_color = Color(0.63, 0.63, 0.63);
Color control_font_low_color = Color(0.69, 0.69, 0.69);
Color control_font_hover_color = Color(0.94, 0.94, 0.94);
+ Color control_font_focus_color = Color(0.94, 0.94, 0.94);
Color control_font_disabled_color = Color(0.9, 0.9, 0.9, 0.2);
Color control_font_pressed_color = Color(1, 1, 1);
@@ -185,6 +186,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "Button", control_font_color);
theme->set_color("font_pressed_color", "Button", control_font_pressed_color);
theme->set_color("font_hover_color", "Button", control_font_hover_color);
+ theme->set_color("font_focus_color", "Button", control_font_focus_color);
theme->set_color("font_hover_pressed_color", "Button", control_font_pressed_color);
theme->set_color("font_disabled_color", "Button", control_font_disabled_color);
theme->set_color("font_outline_color", "Button", Color(1, 1, 1));
@@ -193,6 +195,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("icon_pressed_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_hover_pressed_color", "Button", Color(1, 1, 1, 1));
+ theme->set_color("icon_focus_color", "Button", Color(1, 1, 1, 1));
theme->set_color("icon_disabled_color", "Button", Color(1, 1, 1, 1));
theme->set_constant("hseparation", "Button", 2 * scale);
@@ -207,6 +210,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "LinkButton", control_font_color);
theme->set_color("font_pressed_color", "LinkButton", control_font_pressed_color);
theme->set_color("font_hover_color", "LinkButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "LinkButton", control_font_focus_color);
theme->set_color("font_outline_color", "LinkButton", Color(1, 1, 1));
theme->set_constant("outline_size", "LinkButton", 0);
@@ -245,6 +249,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "OptionButton", control_font_color);
theme->set_color("font_pressed_color", "OptionButton", control_font_pressed_color);
theme->set_color("font_hover_color", "OptionButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "OptionButton", control_font_focus_color);
theme->set_color("font_disabled_color", "OptionButton", control_font_disabled_color);
theme->set_color("font_outline_color", "OptionButton", Color(1, 1, 1));
@@ -266,6 +271,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "MenuButton", control_font_color);
theme->set_color("font_pressed_color", "MenuButton", control_font_pressed_color);
theme->set_color("font_hover_color", "MenuButton", control_font_hover_color);
+ theme->set_color("font_focus_color", "MenuButton", control_font_focus_color);
theme->set_color("font_disabled_color", "MenuButton", Color(1, 1, 1, 0.3));
theme->set_color("font_outline_color", "MenuButton", Color(1, 1, 1));
@@ -308,6 +314,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_pressed_color", "CheckBox", control_font_pressed_color);
theme->set_color("font_hover_color", "CheckBox", control_font_hover_color);
theme->set_color("font_hover_pressed_color", "CheckBox", control_font_pressed_color);
+ theme->set_color("font_focus_color", "CheckBox", control_font_focus_color);
theme->set_color("font_disabled_color", "CheckBox", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckBox", Color(1, 1, 1));
@@ -347,6 +354,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_pressed_color", "CheckButton", control_font_pressed_color);
theme->set_color("font_hover_color", "CheckButton", control_font_hover_color);
theme->set_color("font_hover_pressed_color", "CheckButton", control_font_pressed_color);
+ theme->set_color("font_focus_color", "CheckButton", control_font_focus_color);
theme->set_color("font_disabled_color", "CheckButton", control_font_disabled_color);
theme->set_color("font_outline_color", "CheckButton", Color(1, 1, 1));
@@ -867,6 +875,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color", "ColorPickerButton", Color(1, 1, 1, 1));
theme->set_color("font_pressed_color", "ColorPickerButton", Color(0.8, 0.8, 0.8, 1));
theme->set_color("font_hover_color", "ColorPickerButton", Color(1, 1, 1, 1));
+ theme->set_color("font_focus_color", "ColorPickerButton", Color(1, 1, 1, 1));
theme->set_color("font_disabled_color", "ColorPickerButton", Color(0.9, 0.9, 0.9, 0.3));
theme->set_color("font_outline_color", "ColorPickerButton", Color(1, 1, 1));
@@ -1030,7 +1039,6 @@ void make_default_theme(bool p_hidpi, Ref<Font> p_font) {
dynamic_font_data.instantiate();
dynamic_font_data->set_data_ptr(_font_OpenSans_SemiBold, _font_OpenSans_SemiBold_size);
dynamic_font->add_data(dynamic_font_data);
- dynamic_font->set_base_size(default_font_size);
default_font = dynamic_font;
}
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 04e2b0dc70..819ae95715 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1072,10 +1072,6 @@ void Font::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_data"), &Font::clear_data);
ClassDB::bind_method(D_METHOD("remove_data", "idx"), &Font::remove_data);
- ClassDB::bind_method(D_METHOD("set_base_size", "size"), &Font::set_base_size);
- ClassDB::bind_method(D_METHOD("get_base_size"), &Font::get_base_size);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "base_size"), "set_base_size", "get_base_size");
-
ClassDB::bind_method(D_METHOD("set_variation_coordinates", "variation_coordinates"), &Font::set_variation_coordinates);
ClassDB::bind_method(D_METHOD("get_variation_coordinates"), &Font::get_variation_coordinates);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "variation_coordinates"), "set_variation_coordinates", "get_variation_coordinates");
@@ -1087,20 +1083,20 @@ void Font::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_top"), "set_spacing", "get_spacing", TextServer::SPACING_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "spacing_bottom"), "set_spacing", "get_spacing", TextServer::SPACING_BOTTOM);
- ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("get_height", "size"), &Font::get_height, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_ascent", "size"), &Font::get_ascent, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_descent", "size"), &Font::get_descent, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_underline_position", "size"), &Font::get_underline_position, DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("get_underline_thickness", "size"), &Font::get_underline_thickness, DEFVAL(DEFAULT_FONT_SIZE));
- ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(-1), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(-1), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_string_size", "text", "size", "align", "width", "flags"), &Font::get_string_size, DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("get_multiline_string_size", "text", "width", "size", "flags"), &Font::get_multiline_string_size, DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_string", "canvas_item", "pos", "text", "align", "width", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
+ ClassDB::bind_method(D_METHOD("draw_multiline_string", "canvas_item", "pos", "text", "align", "width", "max_lines", "size", "modulate", "outline_size", "outline_modulate", "flags"), &Font::draw_multiline_string, DEFVAL(HALIGN_LEFT), DEFVAL(-1), DEFVAL(-1), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND));
- ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(-1), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
+ ClassDB::bind_method(D_METHOD("get_char_size", "char", "next", "size"), &Font::get_char_size, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE));
+ ClassDB::bind_method(D_METHOD("draw_char", "canvas_item", "pos", "char", "next", "size", "modulate", "outline_size", "outline_modulate"), &Font::draw_char, DEFVAL(0), DEFVAL(DEFAULT_FONT_SIZE), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(1, 1, 1, 0)));
ClassDB::bind_method(D_METHOD("has_char", "char"), &Font::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &Font::get_supported_chars);
@@ -1195,7 +1191,6 @@ void Font::reset_state() {
data.clear();
rids.clear();
- base_size = 16;
variation_coordinates.clear();
spacing_bottom = 0;
spacing_top = 0;
@@ -1308,14 +1303,6 @@ void Font::remove_data(int p_idx) {
notify_property_list_changed();
}
-void Font::set_base_size(int p_size) {
- base_size = p_size;
-}
-
-int Font::get_base_size() const {
- return base_size;
-}
-
void Font::set_variation_coordinates(const Dictionary &p_variation_coordinates) {
_data_changed();
variation_coordinates = p_variation_coordinates;
@@ -1355,51 +1342,46 @@ int Font::get_spacing(TextServer::SpacingType p_spacing) const {
}
real_t Font::get_height(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
+ ret = MAX(ret, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size));
}
return ret + spacing_bottom + spacing_top;
}
real_t Font::get_ascent(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_ascent(rids[i], size));
+ ret = MAX(ret, TS->font_get_ascent(rids[i], p_size));
}
return ret + spacing_top;
}
real_t Font::get_descent(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_descent(rids[i], size));
+ ret = MAX(ret, TS->font_get_descent(rids[i], p_size));
}
return ret + spacing_bottom;
}
real_t Font::get_underline_position(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_underline_position(rids[i], size));
+ ret = MAX(ret, TS->font_get_underline_position(rids[i], p_size));
}
return ret + spacing_top;
}
real_t Font::get_underline_thickness(int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
real_t ret = 0.f;
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
- ret = MAX(ret, TS->font_get_underline_thickness(rids[i], size));
+ ret = MAX(ret, TS->font_get_underline_thickness(rids[i], p_size));
}
return ret;
}
@@ -1407,8 +1389,6 @@ real_t Font::get_underline_thickness(int p_size) const {
Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, real_t p_width, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1418,14 +1398,14 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
- hash = hash_djb2_one_64(size, hash);
+ hash = hash_djb2_one_64(p_size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
return buffer->get_size();
@@ -1434,8 +1414,6 @@ Size2 Font::get_string_size(const String &p_text, int p_size, HAlign p_align, re
Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int p_size, uint16_t p_flags) const {
ERR_FAIL_COND_V(data.is_empty(), Size2());
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1443,14 +1421,14 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
- wrp_hash = hash_djb2_one_64(size, wrp_hash);
+ wrp_hash = hash_djb2_one_64(p_size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
cache_wrap.insert(wrp_hash, lines_buffer);
@@ -1473,8 +1451,6 @@ Size2 Font::get_multiline_string_size(const String &p_text, real_t p_width, int
void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, real_t p_width, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1484,14 +1460,14 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
hash = hash_djb2_one_64(p_flags, hash);
}
- hash = hash_djb2_one_64(size, hash);
+ hash = hash_djb2_one_64(p_size, hash);
Ref<TextLine> buffer;
if (cache.has(hash)) {
buffer = cache.get(hash);
} else {
buffer.instantiate();
- buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
cache.insert(hash, buffer);
}
@@ -1515,8 +1491,6 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align, float p_width, int p_max_lines, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate, uint16_t p_flags) const {
ERR_FAIL_COND(data.is_empty());
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
}
@@ -1524,14 +1498,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
uint64_t hash = p_text.hash64();
uint64_t wrp_hash = hash_djb2_one_64(hash_djb2_one_float(p_width), hash);
wrp_hash = hash_djb2_one_64(p_flags, wrp_hash);
- wrp_hash = hash_djb2_one_64(size, wrp_hash);
+ wrp_hash = hash_djb2_one_64(p_size, wrp_hash);
Ref<TextParagraph> lines_buffer;
if (cache_wrap.has(wrp_hash)) {
lines_buffer = cache_wrap.get(wrp_hash);
} else {
lines_buffer.instantiate();
- lines_buffer->add_string(p_text, Ref<Font>(this), size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
+ lines_buffer->add_string(p_text, Ref<Font>(this), p_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
lines_buffer->set_width(p_width);
lines_buffer->set_flags(p_flags);
cache_wrap.insert(wrp_hash, lines_buffer);
@@ -1573,16 +1547,14 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
}
Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
- Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], size, glyph_a).x, TS->font_get_ascent(rids[i], size) + TS->font_get_descent(rids[i], size));
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0);
+ Size2 ret = Size2(TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x, TS->font_get_ascent(rids[i], p_size) + TS->font_get_descent(rids[i], p_size));
if ((p_next != 0) && data[i]->has_char(p_next)) {
- int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
- ret.x -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0);
+ ret.x -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x;
}
return ret;
}
@@ -1591,22 +1563,20 @@ Size2 Font::get_char_size(char32_t p_char, char32_t p_next, int p_size) const {
}
real_t Font::draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next, int p_size, const Color &p_modulate, int p_outline_size, const Color &p_outline_modulate) const {
- int size = (p_size <= 0) ? base_size : p_size;
-
for (int i = 0; i < data.size(); i++) {
_ensure_rid(i);
if (data[i]->has_char(p_char)) {
- int32_t glyph_a = TS->font_get_glyph_index(rids[i], size, p_char, 0);
- real_t ret = TS->font_get_glyph_advance(rids[i], size, glyph_a).x;
+ int32_t glyph_a = TS->font_get_glyph_index(rids[i], p_size, p_char, 0);
+ real_t ret = TS->font_get_glyph_advance(rids[i], p_size, glyph_a).x;
if ((p_next != 0) && data[i]->has_char(p_next)) {
- int32_t glyph_b = TS->font_get_glyph_index(rids[i], size, p_next, 0);
- ret -= TS->font_get_kerning(rids[i], size, Vector2i(glyph_a, glyph_b)).x;
+ int32_t glyph_b = TS->font_get_glyph_index(rids[i], p_size, p_next, 0);
+ ret -= TS->font_get_kerning(rids[i], p_size, Vector2i(glyph_a, glyph_b)).x;
}
if (p_outline_size > 0 && p_outline_modulate.a != 0.0f) {
- TS->font_draw_glyph_outline(rids[i], p_canvas_item, size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
+ TS->font_draw_glyph_outline(rids[i], p_canvas_item, p_size, p_outline_size, p_pos, glyph_a, p_outline_modulate);
}
- TS->font_draw_glyph(rids[i], p_canvas_item, size, p_pos, glyph_a, p_modulate);
+ TS->font_draw_glyph(rids[i], p_canvas_item, p_size, p_pos, glyph_a, p_modulate);
return ret;
}
}
diff --git a/scene/resources/font.h b/scene/resources/font.h
index e65fdb0751..e1f1f6d742 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -219,7 +219,6 @@ class Font : public Resource {
mutable Vector<RID> rids;
// Font config.
- int base_size = 16;
Dictionary variation_coordinates;
int spacing_bottom = 0;
int spacing_top = 0;
@@ -237,6 +236,8 @@ protected:
virtual void reset_state() override;
public:
+ static const int DEFAULT_FONT_SIZE = 16;
+
Dictionary get_feature_list() const;
// Font data.
@@ -249,9 +250,6 @@ public:
virtual void remove_data(int p_idx);
// Font configuration.
- virtual void set_base_size(int p_size);
- virtual int get_base_size() const;
-
virtual void set_variation_coordinates(const Dictionary &p_variation_coordinates);
virtual Dictionary get_variation_coordinates() const;
@@ -259,26 +257,26 @@ public:
virtual int get_spacing(TextServer::SpacingType p_spacing) const;
// Font metrics.
- virtual real_t get_height(int p_size = -1) const;
- virtual real_t get_ascent(int p_size = -1) const;
- virtual real_t get_descent(int p_size = -1) const;
- virtual real_t get_underline_position(int p_size = -1) const;
- virtual real_t get_underline_thickness(int p_size = -1) const;
+ virtual real_t get_height(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_ascent(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_descent(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_underline_position(int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t get_underline_thickness(int p_size = DEFAULT_FONT_SIZE) const;
// Drawing string.
- virtual Size2 get_string_size(const String &p_text, int p_size = -1, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = -1, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
+ virtual Size2 get_string_size(const String &p_text, int p_size = DEFAULT_FONT_SIZE, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual Size2 get_multiline_string_size(const String &p_text, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
- virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
- virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
+ virtual void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, real_t p_width = -1, int p_max_lines = -1, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint16_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;
// Helper functions.
virtual bool has_char(char32_t p_char) const;
virtual String get_supported_chars() const;
// Drawing char.
- virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
- virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
+ virtual Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE) const;
+ virtual real_t draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;
Vector<RID> get_rids() const;
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index abb3381c4e..267180973f 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1100,7 +1100,7 @@ void BaseMaterial3D::_update_shader() {
if (proximity_fade_enabled) {
code += " float depth_tex = textureLod(DEPTH_TEXTURE,SCREEN_UV,0.0).r;\n";
- code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex*2.0-1.0,1.0);\n";
+ code += " vec4 world_pos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV*2.0-1.0,depth_tex,1.0);\n";
code += " world_pos.xyz/=world_pos.w;\n";
code += " ALPHA*=clamp(1.0-smoothstep(world_pos.z+proximity_fade_distance,world_pos.z,VERTEX.z),0.0,1.0);\n";
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 8d5571d67c..a95b4d4a5e 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -278,7 +278,6 @@ public:
int surface_get_array_index_len(int p_idx) const override;
uint32_t surface_get_format(int p_idx) const override;
PrimitiveType surface_get_primitive_type(int p_idx) const override;
- bool surface_is_alpha_sorting_enabled(int p_idx) const;
virtual void surface_set_material(int p_idx, const Ref<Material> &p_material) override;
virtual Ref<Material> surface_get_material(int p_idx) const override;
diff --git a/scene/resources/shape_2d.h b/scene/resources/shape_2d.h
index 14bdd60e4b..7c5d1344e8 100644
--- a/scene/resources/shape_2d.h
+++ b/scene/resources/shape_2d.h
@@ -64,7 +64,6 @@ public:
static bool is_collision_outline_enabled();
- Shape2D();
~Shape2D();
};
diff --git a/scene/resources/skeleton_modification_stack_3d.cpp b/scene/resources/skeleton_modification_stack_3d.cpp
index a724b732b9..c03210cf48 100644
--- a/scene/resources/skeleton_modification_stack_3d.cpp
+++ b/scene/resources/skeleton_modification_stack_3d.cpp
@@ -125,6 +125,7 @@ Ref<SkeletonModification3D> SkeletonModificationStack3D::get_modification(int p_
}
void SkeletonModificationStack3D::add_modification(Ref<SkeletonModification3D> p_mod) {
+ ERR_FAIL_NULL(p_mod);
p_mod->_setup_modification(this);
modifications.push_back(p_mod);
}
diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp
index 65cdc1e24e..99977a20f2 100644
--- a/scene/resources/theme.cpp
+++ b/scene/resources/theme.cpp
@@ -169,7 +169,7 @@ void Theme::_get_property_list(List<PropertyInfo> *p_list) const {
const StringName *key2 = nullptr;
while ((key2 = font_size_map[*key].next(key2))) {
- list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2));
+ list.push_back(PropertyInfo(Variant::INT, String() + *key + "/font_sizes/" + *key2, PROPERTY_HINT_RANGE, "0,256,1,or_greater"));
}
}
@@ -1655,7 +1655,7 @@ void Theme::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_base_scale", PROPERTY_HINT_RANGE, "0.0,2.0,0.01,or_greater"), "set_default_base_scale", "get_default_base_scale");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "default_font", PROPERTY_HINT_RESOURCE_TYPE, "Font"), "set_default_font", "get_default_font");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size"), "set_default_font_size", "get_default_font_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "default_font_size", PROPERTY_HINT_RANGE, "0,256,1,or_greater"), "set_default_font_size", "get_default_font_size");
BIND_ENUM_CONSTANT(DATA_TYPE_COLOR);
BIND_ENUM_CONSTANT(DATA_TYPE_CONSTANT);
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index a45b6b8eb6..141e9e1b0e 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -301,6 +301,66 @@ int TileSet::get_next_source_id() const {
return next_source_id;
}
+void TileSet::_update_terrains_cache() {
+ if (terrains_cache_dirty) {
+ // Organizes tiles into structures.
+ per_terrain_pattern_tiles.resize(terrain_sets.size());
+ for (int i = 0; i < (int)per_terrain_pattern_tiles.size(); i++) {
+ per_terrain_pattern_tiles[i].clear();
+ }
+
+ for (const KeyValue<int, Ref<TileSetSource>> &kv : sources) {
+ Ref<TileSetSource> source = kv.value;
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ for (int tile_index = 0; tile_index < source->get_tiles_count(); tile_index++) {
+ Vector2i tile_id = source->get_tile_id(tile_index);
+ for (int alternative_index = 0; alternative_index < source->get_alternative_tiles_count(tile_id); alternative_index++) {
+ int alternative_id = source->get_alternative_tile_id(tile_id, alternative_index);
+
+ // Executed for each tile_data.
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
+ int terrain_set = tile_data->get_terrain_set();
+ if (terrain_set >= 0) {
+ TileMapCell cell;
+ cell.source_id = kv.key;
+ cell.set_atlas_coords(tile_id);
+ cell.alternative_tile = alternative_id;
+
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
+
+ // Terrain bits.
+ for (int i = 0; i < terrains_pattern.size(); i++) {
+ int terrain = terrains_pattern[i];
+ if (terrain >= 0) {
+ per_terrain_pattern_tiles[terrain_set][terrains_pattern].insert(cell);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Add the empty cell in the possible patterns and cells.
+ for (int i = 0; i < terrain_sets.size(); i++) {
+ TileSet::TerrainsPattern empty_pattern;
+ for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
+ if (is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
+ empty_pattern.push_back(-1);
+ }
+ }
+
+ TileMapCell empty_cell;
+ empty_cell.source_id = TileSet::INVALID_SOURCE;
+ empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
+ empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
+ per_terrain_pattern_tiles[i][empty_pattern].insert(empty_cell);
+ }
+ terrains_cache_dirty = false;
+ }
+}
+
void TileSet::_compute_next_source_id() {
while (sources.has(next_source_id)) {
next_source_id = (next_source_id + 1) % 1073741824; // 2 ** 30
@@ -321,6 +381,7 @@ int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source
sources[new_source_id]->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &TileSet::_source_changed));
+ terrains_cache_dirty = true;
emit_changed();
return new_source_id;
@@ -336,6 +397,7 @@ void TileSet::remove_source(int p_source_id) {
source_ids.erase(p_source_id);
source_ids.sort();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -357,6 +419,7 @@ void TileSet::set_source_id(int p_source_id, int p_new_source_id) {
_compute_next_source_id();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -545,6 +608,7 @@ void TileSet::add_terrain_set(int p_index) {
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -557,6 +621,7 @@ void TileSet::move_terrain_set(int p_from_index, int p_to_pos) {
source.value->move_terrain_set(p_from_index, p_to_pos);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -567,6 +632,7 @@ void TileSet::remove_terrain_set(int p_index) {
source.value->remove_terrain_set(p_index);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -578,6 +644,7 @@ void TileSet::set_terrain_set_mode(int p_terrain_set, TerrainMode p_terrain_mode
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -612,6 +679,7 @@ void TileSet::add_terrain(int p_terrain_set, int p_index) {
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -627,6 +695,7 @@ void TileSet::move_terrain(int p_terrain_set, int p_from_index, int p_to_pos) {
source.value->move_terrain(p_terrain_set, p_from_index, p_to_pos);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -640,6 +709,7 @@ void TileSet::remove_terrain(int p_terrain_set, int p_index) {
source.value->remove_terrain(p_terrain_set, p_index);
}
notify_property_list_changed();
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -1196,6 +1266,73 @@ int TileSet::get_patterns_count() {
return patterns.size();
}
+Set<TileSet::TerrainsPattern> TileSet::get_terrains_pattern_set(int p_terrain_set) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileSet::TerrainsPattern>());
+ _update_terrains_cache();
+
+ Set<TileSet::TerrainsPattern> output;
+ for (KeyValue<TileSet::TerrainsPattern, Set<TileMapCell>> kv : per_terrain_pattern_tiles[p_terrain_set]) {
+ output.insert(kv.key);
+ }
+ return output;
+}
+
+Set<TileMapCell> TileSet::get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), Set<TileMapCell>());
+ _update_terrains_cache();
+ return per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+}
+
+TileMapCell TileSet::get_random_tile_from_pattern(int p_terrain_set, TileSet::TerrainsPattern p_terrain_tile_pattern) {
+ ERR_FAIL_INDEX_V(p_terrain_set, terrain_sets.size(), TileMapCell());
+ _update_terrains_cache();
+
+ // Count the sum of probabilities.
+ double sum = 0.0;
+ Set<TileMapCell> set = per_terrain_pattern_tiles[p_terrain_set][p_terrain_tile_pattern];
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = sources[E->get().source_id];
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ sum += tile_data->get_probability();
+ } else {
+ sum += 1.0;
+ }
+ } else {
+ sum += 1.0;
+ }
+ }
+
+ // Generate a random number.
+ double count = 0.0;
+ double picked = Math::random(0.0, sum);
+
+ // Pick the tile.
+ for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
+ if (E->get().source_id >= 0) {
+ Ref<TileSetSource> source = sources[E->get().source_id];
+
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ count += tile_data->get_probability();
+ } else {
+ count += 1.0;
+ }
+ } else {
+ count += 1.0;
+ }
+
+ if (count >= picked) {
+ return E->get();
+ }
+ }
+
+ ERR_FAIL_V(TileMapCell());
+}
+
Vector<Vector2> TileSet::get_tile_shape_polygon() {
Vector<Vector2> points;
if (tile_shape == TileSet::TILE_SHAPE_SQUARE) {
@@ -1510,6 +1647,7 @@ Vector<Vector<Ref<Texture2D>>> TileSet::generate_terrains_icons(Size2i p_size) {
}
void TileSet::_source_changed() {
+ terrains_cache_dirty = true;
emit_changed();
}
@@ -4500,6 +4638,37 @@ bool TileData::is_allowing_transform() const {
return allow_transform;
}
+TileData *TileData::duplicate() {
+ TileData *output = memnew(TileData);
+ output->tile_set = tile_set;
+
+ output->allow_transform = allow_transform;
+
+ // Rendering
+ output->flip_h = flip_h;
+ output->flip_v = flip_v;
+ output->transpose = transpose;
+ output->tex_offset = tex_offset;
+ output->material = material;
+ output->modulate = modulate;
+ output->z_index = z_index;
+ output->y_sort_origin = y_sort_origin;
+ output->occluders = occluders;
+ // Physics
+ output->physics = physics;
+ // Terrain
+ output->terrain_set = -1;
+ memcpy(output->terrain_peering_bits, terrain_peering_bits, 16 * sizeof(int));
+ // Navigation
+ output->navigation = navigation;
+ // Misc
+ output->probability = probability;
+ // Custom data
+ output->custom_data = custom_data;
+
+ return output;
+}
+
// Rendering
void TileData::set_flip_h(bool p_flip_h) {
ERR_FAIL_COND_MSG(!allow_transform && p_flip_h, "Transform is only allowed for alternative tiles (with its alternative_id != 0)");
@@ -4743,6 +4912,18 @@ bool TileData::is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit)
return tile_set->is_valid_peering_bit_terrain(terrain_set, p_peering_bit);
}
+TileSet::TerrainsPattern TileData::get_terrains_pattern() const {
+ ERR_FAIL_COND_V(!tile_set, TileSet::TerrainsPattern());
+
+ TileSet::TerrainsPattern output;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (tile_set->is_valid_peering_bit_terrain(terrain_set, TileSet::CellNeighbor(i))) {
+ output.push_back(get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+ }
+ }
+ return output;
+}
+
// Navigation
void TileData::set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon) {
ERR_FAIL_INDEX(p_layer_id, navigation.size());
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 530c90920f..077315e58d 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -253,6 +253,7 @@ public:
Ref<PackedScene> scene;
Vector2 offset;
};
+ typedef Array TerrainsPattern;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -303,6 +304,10 @@ private:
Map<TerrainMode, Map<CellNeighbor, Ref<ArrayMesh>>> terrain_bits_meshes;
bool terrain_bits_meshes_dirty = true;
+ LocalVector<Map<TileSet::TerrainsPattern, Set<TileMapCell>>> per_terrain_pattern_tiles; // Cached data.
+ bool terrains_cache_dirty = true;
+ void _update_terrains_cache();
+
// Navigation
struct NavigationLayer {
uint32_t layers = 1;
@@ -470,6 +475,11 @@ public:
void remove_pattern(int p_index);
int get_patterns_count();
+ // Terrains.
+ Set<TerrainsPattern> get_terrains_pattern_set(int p_terrain_set);
+ Set<TileMapCell> get_tiles_for_terrains_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+ TileMapCell get_random_tile_from_pattern(int p_terrain_set, TerrainsPattern p_terrain_tile_pattern);
+
// Helpers
Vector<Vector2> get_tile_shape_polygon();
void draw_tile_shape(CanvasItem *p_canvas_item, Transform2D p_transform, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>());
@@ -784,6 +794,9 @@ public:
void set_allow_transform(bool p_allow_transform);
bool is_allowing_transform() const;
+ // To duplicate a TileData object, needed for runtiume update.
+ TileData *duplicate();
+
// Rendering
void set_flip_h(bool p_flip_h);
bool get_flip_h() const;
@@ -831,6 +844,8 @@ public:
int get_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
bool is_valid_peering_bit_terrain(TileSet::CellNeighbor p_peering_bit) const;
+ TileSet::TerrainsPattern get_terrains_pattern() const; // Not exposed.
+
// Navigation
void set_navigation_polygon(int p_layer_id, Ref<NavigationPolygon> p_navigation_polygon);
Ref<NavigationPolygon> get_navigation_polygon(int p_layer_id) const;
diff --git a/servers/audio/effects/audio_effect_limiter.h b/servers/audio/effects/audio_effect_limiter.h
index d5def670a4..398613aa44 100644
--- a/servers/audio/effects/audio_effect_limiter.h
+++ b/servers/audio/effects/audio_effect_limiter.h
@@ -72,8 +72,6 @@ public:
float get_soft_clip_ratio() const;
Ref<AudioEffectInstance> instantiate() override;
- void set_volume_db(float p_volume);
- float get_volume_db() const;
AudioEffectLimiter();
};
diff --git a/servers/audio/effects/audio_effect_record.h b/servers/audio/effects/audio_effect_record.h
index 1a89821f80..6e862b1377 100644
--- a/servers/audio/effects/audio_effect_record.h
+++ b/servers/audio/effects/audio_effect_record.h
@@ -93,7 +93,6 @@ class AudioEffectRecord : public AudioEffect {
protected:
static void _bind_methods();
- static void debug(uint64_t time_diff, int p_frame_count);
public:
Ref<AudioEffectInstance> instantiate() override;
diff --git a/servers/audio/effects/audio_effect_reverb.h b/servers/audio/effects/audio_effect_reverb.h
index d01d1120bd..eaa66352f6 100644
--- a/servers/audio/effects/audio_effect_reverb.h
+++ b/servers/audio/effects/audio_effect_reverb.h
@@ -90,8 +90,6 @@ public:
float get_hpf() const;
Ref<AudioEffectInstance> instantiate() override;
- void set_volume_db(float p_volume);
- float get_volume_db() const;
AudioEffectReverb();
};
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index a6101530c8..3fb47e8f05 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -497,6 +497,7 @@ void DisplayServer::_bind_methods() {
BIND_ENUM_CONSTANT(FEATURE_NATIVE_ICON);
BIND_ENUM_CONSTANT(FEATURE_ORIENTATION);
BIND_ENUM_CONSTANT(FEATURE_SWAP_BUFFERS);
+ BIND_ENUM_CONSTANT(FEATURE_CLIPBOARD_PRIMARY);
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
diff --git a/servers/display_server.h b/servers/display_server.h
index 8af7946a1e..2595cf2eb8 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -114,6 +114,7 @@ public:
FEATURE_ORIENTATION,
FEATURE_SWAP_BUFFERS,
FEATURE_KEEP_SCREEN_ON,
+ FEATURE_CLIPBOARD_PRIMARY,
};
virtual bool has_feature(Feature p_feature) const = 0;
diff --git a/servers/physics_2d/godot_body_2d.cpp b/servers/physics_2d/godot_body_2d.cpp
index b0885a1f7b..56f191c203 100644
--- a/servers/physics_2d/godot_body_2d.cpp
+++ b/servers/physics_2d/godot_body_2d.cpp
@@ -55,7 +55,7 @@ void GodotBody2D::update_mass_properties() {
if (calculate_center_of_mass) {
// We have to recompute the center of mass.
- center_of_mass = Vector2();
+ center_of_mass_local = Vector2();
if (total_area != 0.0) {
for (int i = 0; i < get_shape_count(); i++) {
@@ -68,10 +68,10 @@ void GodotBody2D::update_mass_properties() {
real_t mass = area * this->mass / total_area;
// NOTE: we assume that the shape origin is also its center of mass.
- center_of_mass += mass * get_shape_transform(i).get_origin();
+ center_of_mass_local += mass * get_shape_transform(i).get_origin();
}
- center_of_mass /= mass;
+ center_of_mass_local /= mass;
}
}
@@ -94,7 +94,7 @@ void GodotBody2D::update_mass_properties() {
Transform2D mtx = get_shape_transform(i);
Vector2 scale = mtx.get_scale();
- Vector2 shape_origin = mtx.get_origin() - center_of_mass;
+ Vector2 shape_origin = mtx.get_origin() - center_of_mass_local;
inertia += shape->get_moment_of_inertia(mass, scale) + mass * shape_origin.length_squared();
}
}
@@ -119,6 +119,8 @@ void GodotBody2D::update_mass_properties() {
} break;
}
+
+ _update_transform_dependent();
}
void GodotBody2D::reset_mass_properties() {
@@ -179,7 +181,8 @@ void GodotBody2D::set_param(PhysicsServer2D::BodyParameter p_param, const Varian
} break;
case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: {
calculate_center_of_mass = false;
- center_of_mass = p_value;
+ center_of_mass_local = p_value;
+ _update_transform_dependent();
} break;
case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: {
gravity_scale = p_value;
@@ -273,6 +276,7 @@ PhysicsServer2D::BodyMode GodotBody2D::get_mode() const {
void GodotBody2D::_shapes_changed() {
_mass_properties_changed();
+ wakeup();
wakeup_neighbours();
}
@@ -301,6 +305,7 @@ void GodotBody2D::set_state(PhysicsServer2D::BodyState p_state, const Variant &p
}
_set_transform(t);
_set_inv_transform(get_transform().inverse());
+ _update_transform_dependent();
}
wakeup();
@@ -400,6 +405,10 @@ void GodotBody2D::_compute_area_gravity_and_damping(const GodotArea2D *p_area) {
area_angular_damp += p_area->get_angular_damp();
}
+void GodotBody2D::_update_transform_dependent() {
+ center_of_mass = get_transform().basis_xform(center_of_mass_local);
+}
+
void GodotBody2D::integrate_forces(real_t p_step) {
if (mode == PhysicsServer2D::BODY_MODE_STATIC) {
return;
@@ -549,17 +558,13 @@ void GodotBody2D::integrate_velocities(real_t p_step) {
real_t total_angular_velocity = angular_velocity + biased_angular_velocity;
Vector2 total_linear_velocity = linear_velocity + biased_linear_velocity;
- real_t angle = get_transform().get_rotation() + total_angular_velocity * p_step;
+ real_t angle_delta = total_angular_velocity * p_step;
+ real_t angle = get_transform().get_rotation() + angle_delta;
Vector2 pos = get_transform().get_origin() + total_linear_velocity * p_step;
- real_t center_of_mass_distance = center_of_mass.length();
- if (center_of_mass_distance > CMP_EPSILON) {
+ if (center_of_mass.length_squared() > CMP_EPSILON2) {
// Calculate displacement due to center of mass offset.
- real_t prev_angle = get_transform().get_rotation();
- real_t angle_base = Math::atan2(center_of_mass.y, center_of_mass.x);
- Vector2 point1(Math::cos(angle_base + prev_angle), Math::sin(angle_base + prev_angle));
- Vector2 point2(Math::cos(angle_base + angle), Math::sin(angle_base + angle));
- pos += center_of_mass_distance * (point1 - point2);
+ pos += center_of_mass - center_of_mass.rotated(angle_delta);
}
_set_transform(Transform2D(angle, pos), continuous_cd_mode == PhysicsServer2D::CCD_MODE_DISABLED);
@@ -568,6 +573,8 @@ void GodotBody2D::integrate_velocities(real_t p_step) {
if (continuous_cd_mode != PhysicsServer2D::CCD_MODE_DISABLED) {
new_transform = get_transform();
}
+
+ _update_transform_dependent();
}
void GodotBody2D::wakeup_neighbours() {
diff --git a/servers/physics_2d/godot_body_2d.h b/servers/physics_2d/godot_body_2d.h
index c4f3578f1b..5fce362fa7 100644
--- a/servers/physics_2d/godot_body_2d.h
+++ b/servers/physics_2d/godot_body_2d.h
@@ -66,6 +66,7 @@ class GodotBody2D : public GodotCollisionObject2D {
real_t inertia = 0.0;
real_t _inv_inertia = 0.0;
+ Vector2 center_of_mass_local;
Vector2 center_of_mass;
bool calculate_inertia = true;
@@ -139,7 +140,9 @@ class GodotBody2D : public GodotCollisionObject2D {
uint64_t island_step = 0;
- _FORCE_INLINE_ void _compute_area_gravity_and_damping(const GodotArea2D *p_area);
+ void _compute_area_gravity_and_damping(const GodotArea2D *p_area);
+
+ void _update_transform_dependent();
friend class GodotPhysicsDirectBodyState2D; // i give up, too many functions to expose
diff --git a/servers/physics_3d/godot_body_3d.cpp b/servers/physics_3d/godot_body_3d.cpp
index 6df6a0c45b..02929eeaed 100644
--- a/servers/physics_3d/godot_body_3d.cpp
+++ b/servers/physics_3d/godot_body_3d.cpp
@@ -40,7 +40,7 @@ void GodotBody3D::_mass_properties_changed() {
}
}
-void GodotBody3D::_update_transform_dependant() {
+void GodotBody3D::_update_transform_dependent() {
center_of_mass = get_transform().basis.xform(center_of_mass_local);
principal_inertia_axes = get_transform().basis * principal_inertia_axes_local;
@@ -161,7 +161,7 @@ void GodotBody3D::update_mass_properties() {
} break;
}
- _update_transform_dependant();
+ _update_transform_dependent();
}
void GodotBody3D::reset_mass_properties() {
@@ -217,14 +217,14 @@ void GodotBody3D::set_param(PhysicsServer3D::BodyParameter p_param, const Varian
if (mode == PhysicsServer3D::BODY_MODE_DYNAMIC) {
principal_inertia_axes_local = Basis();
_inv_inertia = inertia.inverse();
- _update_transform_dependant();
+ _update_transform_dependent();
}
}
} break;
case PhysicsServer3D::BODY_PARAM_CENTER_OF_MASS: {
calculate_center_of_mass = false;
center_of_mass_local = p_value;
- _update_transform_dependant();
+ _update_transform_dependent();
} break;
case PhysicsServer3D::BODY_PARAM_GRAVITY_SCALE: {
gravity_scale = p_value;
@@ -295,7 +295,7 @@ void GodotBody3D::set_mode(PhysicsServer3D::BodyMode p_mode) {
if (mode == PhysicsServer3D::BODY_MODE_KINEMATIC && prev != mode) {
first_time_kinematic = true;
}
- _update_transform_dependant();
+ _update_transform_dependent();
} break;
case PhysicsServer3D::BODY_MODE_DYNAMIC: {
@@ -303,7 +303,7 @@ void GodotBody3D::set_mode(PhysicsServer3D::BodyMode p_mode) {
if (!calculate_inertia) {
principal_inertia_axes_local = Basis();
_inv_inertia = inertia.inverse();
- _update_transform_dependant();
+ _update_transform_dependent();
}
_mass_properties_changed();
_set_static(false);
@@ -314,7 +314,7 @@ void GodotBody3D::set_mode(PhysicsServer3D::BodyMode p_mode) {
_inv_mass = mass > 0 ? (1.0 / mass) : 0;
_inv_inertia = Vector3();
angular_velocity = Vector3();
- _update_transform_dependant();
+ _update_transform_dependent();
_set_static(false);
set_active(true);
}
@@ -327,6 +327,8 @@ PhysicsServer3D::BodyMode GodotBody3D::get_mode() const {
void GodotBody3D::_shapes_changed() {
_mass_properties_changed();
+ wakeup();
+ wakeup_neighbours();
}
void GodotBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p_variant) {
@@ -355,6 +357,7 @@ void GodotBody3D::set_state(PhysicsServer3D::BodyState p_state, const Variant &p
}
_set_transform(t);
_set_inv_transform(get_transform().inverse());
+ _update_transform_dependent();
}
wakeup();
@@ -651,7 +654,7 @@ void GodotBody3D::integrate_velocities(real_t p_step) {
_set_transform(transform);
_set_inv_transform(get_transform().inverse());
- _update_transform_dependant();
+ _update_transform_dependent();
}
void GodotBody3D::wakeup_neighbours() {
diff --git a/servers/physics_3d/godot_body_3d.h b/servers/physics_3d/godot_body_3d.h
index 1151a22c96..5acdab9d13 100644
--- a/servers/physics_3d/godot_body_3d.h
+++ b/servers/physics_3d/godot_body_3d.h
@@ -135,9 +135,9 @@ class GodotBody3D : public GodotCollisionObject3D {
uint64_t island_step = 0;
- _FORCE_INLINE_ void _compute_area_gravity_and_damping(const GodotArea3D *p_area);
+ void _compute_area_gravity_and_damping(const GodotArea3D *p_area);
- _FORCE_INLINE_ void _update_transform_dependant();
+ void _update_transform_dependent();
friend class GodotPhysicsDirectBodyState3D; // i give up, too many functions to expose
diff --git a/servers/physics_3d/godot_physics_server_3d.cpp b/servers/physics_3d/godot_physics_server_3d.cpp
index 34b56e733e..79a2e0b0ea 100644
--- a/servers/physics_3d/godot_physics_server_3d.cpp
+++ b/servers/physics_3d/godot_physics_server_3d.cpp
@@ -584,7 +584,6 @@ void GodotPhysicsServer3D::body_set_collision_layer(RID p_body, uint32_t p_layer
ERR_FAIL_COND(!body);
body->set_collision_layer(p_layer);
- body->wakeup();
}
uint32_t GodotPhysicsServer3D::body_get_collision_layer(RID p_body) const {
@@ -599,7 +598,6 @@ void GodotPhysicsServer3D::body_set_collision_mask(RID p_body, uint32_t p_mask)
ERR_FAIL_COND(!body);
body->set_collision_mask(p_mask);
- body->wakeup();
}
uint32_t GodotPhysicsServer3D::body_get_collision_mask(RID p_body) const {
diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp
index d15235d27c..f214e3603a 100644
--- a/servers/physics_3d/godot_soft_body_3d.cpp
+++ b/servers/physics_3d/godot_soft_body_3d.cpp
@@ -139,6 +139,7 @@ void GodotSoftBody3D::set_mesh(RID p_mesh) {
}
Array arrays = RenderingServer::get_singleton()->mesh_surface_get_arrays(soft_mesh, 0);
+ ERR_FAIL_COND(arrays.is_empty());
bool success = create_from_trimesh(arrays[RenderingServer::ARRAY_INDEX], arrays[RenderingServer::ARRAY_VERTEX]);
if (!success) {
diff --git a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp b/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp
index d7e0537439..b88e2d1190 100644
--- a/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp
+++ b/servers/physics_3d/joints/godot_generic_6dof_joint_3d.cpp
@@ -232,7 +232,7 @@ GodotGeneric6DOFJoint3D::GodotGeneric6DOFJoint3D(GodotBody3D *rbA, GodotBody3D *
void GodotGeneric6DOFJoint3D::calculateAngleInfo() {
Basis relative_frame = m_calculatedTransformB.basis.inverse() * m_calculatedTransformA.basis;
- m_calculatedAxisAngleDiff = relative_frame.get_euler_xyz();
+ m_calculatedAxisAngleDiff = relative_frame.get_euler(Basis::EULER_ORDER_XYZ);
// in euler angle mode we do not actually constrain the angular velocity
// along the axes axis[0] and axis[2] (although we do use axis[1]) :
diff --git a/servers/physics_server_2d_wrap_mt.h b/servers/physics_server_2d_wrap_mt.h
index 927ef5d57c..4a2f07ab1e 100644
--- a/servers/physics_server_2d_wrap_mt.h
+++ b/servers/physics_server_2d_wrap_mt.h
@@ -59,9 +59,7 @@ class PhysicsServer2DWrapMT : public PhysicsServer2D {
bool create_thread = false;
Semaphore step_sem;
- int step_pending = 0;
void thread_step(real_t p_delta);
- void thread_flush();
void thread_exit();
diff --git a/servers/physics_server_3d_wrap_mt.h b/servers/physics_server_3d_wrap_mt.h
index bf936fd0fc..4c88ef2642 100644
--- a/servers/physics_server_3d_wrap_mt.h
+++ b/servers/physics_server_3d_wrap_mt.h
@@ -58,9 +58,7 @@ class PhysicsServer3DWrapMT : public PhysicsServer3D {
bool create_thread = false;
Semaphore step_sem;
- int step_pending = 0;
void thread_step(real_t p_delta);
- void thread_flush();
void thread_exit();
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index a1c3481ed6..d05cfdc386 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -657,6 +657,10 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
actions.render_mode_defines["specular_schlick_ggx"] = "#define SPECULAR_BLINN\n";
}
+ actions.custom_samplers["SCREEN_TEXTURE"] = "material_samplers[3]"; // linear filter with mipmaps
+ actions.custom_samplers["DEPTH_TEXTURE"] = "material_samplers[3]";
+ actions.custom_samplers["NORMAL_ROUGHNESS_TEXTURE"] = "material_samplers[1]"; // linear filter
+
actions.render_mode_defines["specular_blinn"] = "#define SPECULAR_BLINN\n";
actions.render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n";
actions.render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index d128578d0b..a906a853dc 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -2696,9 +2696,14 @@ void RendererSceneRenderRD::shadows_quality_set(RS::ShadowQuality p_quality) {
switch (shadows_quality) {
case RS::SHADOW_QUALITY_HARD: {
penumbra_shadow_samples = 4;
- soft_shadow_samples = 1;
+ soft_shadow_samples = 0;
shadows_quality_radius = 1.0;
} break;
+ case RS::SHADOW_QUALITY_SOFT_VERY_LOW: {
+ penumbra_shadow_samples = 4;
+ soft_shadow_samples = 1;
+ shadows_quality_radius = 1.5;
+ } break;
case RS::SHADOW_QUALITY_SOFT_LOW: {
penumbra_shadow_samples = 8;
soft_shadow_samples = 4;
@@ -2738,9 +2743,14 @@ void RendererSceneRenderRD::directional_shadow_quality_set(RS::ShadowQuality p_q
switch (directional_shadow_quality) {
case RS::SHADOW_QUALITY_HARD: {
directional_penumbra_shadow_samples = 4;
- directional_soft_shadow_samples = 1;
+ directional_soft_shadow_samples = 0;
directional_shadow_quality_radius = 1.0;
} break;
+ case RS::SHADOW_QUALITY_SOFT_VERY_LOW: {
+ directional_penumbra_shadow_samples = 4;
+ directional_soft_shadow_samples = 1;
+ directional_shadow_quality_radius = 1.5;
+ } break;
case RS::SHADOW_QUALITY_SOFT_LOW: {
directional_penumbra_shadow_samples = 8;
directional_soft_shadow_samples = 4;
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index a7bfea455d..3e68a2b622 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -2672,9 +2672,11 @@ void RendererStorageRD::MaterialData::update_textures(const Map<StringName, Vari
if (uniform_array_size > 0) {
if (textures.size() < uniform_array_size) {
const Map<StringName, RID>::Element *W = p_default_textures.find(uniform_name);
- if (W) {
- for (int j = textures.size(); j < uniform_array_size; j++) {
+ for (int j = textures.size(); j < uniform_array_size; j++) {
+ if (W) {
textures.push_back(W->get());
+ } else {
+ textures.push_back(RID());
}
}
}
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index 987960069b..5bb8fea89a 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -1251,6 +1251,7 @@ void main() {
{ //directional light
+#ifndef SHADOWS_DISABLED
// Do shadow and lighting in two passes to reduce register pressure
uint shadow0 = 0;
uint shadow1 = 0;
@@ -1449,6 +1450,7 @@ void main() {
shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8);
}
}
+#endif // SHADOWS_DISABLED
for (uint i = 0; i < 8; i++) {
if (i >= scene_data.directional_light_count) {
@@ -1511,12 +1513,13 @@ void main() {
#endif
float shadow = 1.0;
-
+#ifndef SHADOWS_DISABLED
if (i < 4) {
shadow = float(shadow0 >> (i * 8) & 0xFF) / 255.0;
} else {
shadow = float(shadow1 >> ((i - 4) * 8) & 0xFF) / 255.0;
}
+#endif
blur_shadow(shadow);
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
index 4d466342f8..61559fe809 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_lights_inc.glsl
@@ -287,7 +287,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte
#endif //defined(LIGHT_CODE_USED)
}
-#ifndef USE_NO_SHADOWS
+#ifndef SHADOWS_DISABLED
// Interleaved Gradient Noise
// https://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
@@ -301,7 +301,7 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
float depth = coord.z;
//if only one sample is taken, take it from the center
- if (sc_directional_soft_shadow_samples == 1) {
+ if (sc_directional_soft_shadow_samples == 0) {
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}
@@ -327,7 +327,7 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
float depth = coord.z;
//if only one sample is taken, take it from the center
- if (sc_soft_shadow_samples == 1) {
+ if (sc_soft_shadow_samples == 0) {
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
}
@@ -350,7 +350,7 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec4 uv_rect, vec2 flip_offset, float depth) {
//if only one sample is taken, take it from the center
- if (sc_soft_shadow_samples == 1) {
+ if (sc_soft_shadow_samples == 0) {
vec2 pos = coord * 0.5 + 0.5;
pos = uv_rect.xy + pos * uv_rect.zw;
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
@@ -433,7 +433,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
}
}
-#endif //USE_NO_SHADOWS
+#endif // SHADOWS_DISABLED
float get_omni_attenuation(float distance, float inv_range, float decay) {
float nd = distance * inv_range;
@@ -445,7 +445,7 @@ float get_omni_attenuation(float distance, float inv_range, float decay) {
}
float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
-#ifndef USE_NO_SHADOWS
+#ifndef SHADOWS_DISABLED
if (omni_lights.data[idx].shadow_enabled) {
// there is a shadowmap
vec2 texel_size = scene_data.shadow_atlas_pixel_size;
@@ -730,7 +730,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
}
float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
-#ifndef USE_NO_SHADOWS
+#ifndef SHADOWS_DISABLED
if (spot_lights.data[idx].shadow_enabled) {
vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
float light_length = length(light_rel_vec);
@@ -806,7 +806,7 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
return shadow;
}
-#endif //USE_NO_SHADOWS
+#endif // SHADOWS_DISABLED
return 1.0;
}
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index 0ee68d5e10..2b59e85e4f 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -1046,7 +1046,7 @@ void main() {
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
if (!sc_disable_directional_lights) { //directional light
-
+#ifndef SHADOWS_DISABLED
// Do shadow and lighting in two passes to reduce register pressure
uint shadow0 = 0;
uint shadow1 = 0;
@@ -1322,6 +1322,8 @@ void main() {
}
}
+#endif // SHADOWS_DISABLED
+
for (uint i = 0; i < 8; i++) {
if (i >= scene_data.directional_light_count) {
break;
@@ -1334,13 +1336,13 @@ void main() {
// We're not doing light transmittence
float shadow = 1.0;
-
+#ifndef SHADOWS_DISABLED
if (i < 4) {
shadow = float(shadow0 >> (i * 8) & 0xFF) / 255.0;
} else {
shadow = float(shadow1 >> ((i - 4) * 8) & 0xFF) / 255.0;
}
-
+#endif
blur_shadow(shadow);
light_compute(normal, directional_lights.data[i].direction, normalize(view), 0.0, directional_lights.data[i].color * directional_lights.data[i].energy, shadow, f0, orms, 1.0,
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index a7886bb6b1..cf422962f0 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1139,7 +1139,7 @@ void RendererSceneCull::instance_geometry_set_cast_shadows_setting(RID p_instanc
if (instance->scenario && instance->array_index >= 0) {
InstanceData &idata = instance->scenario->instance_data[instance->array_index];
- if (instance->cast_shadows != RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
+ if (instance->cast_shadows != RS::SHADOW_CASTING_SETTING_OFF) {
idata.flags |= InstanceData::FLAG_CAST_SHADOWS;
} else {
idata.flags &= ~uint32_t(InstanceData::FLAG_CAST_SHADOWS);
@@ -1603,7 +1603,7 @@ void RendererSceneCull::_update_instance(Instance *p_instance) {
//always dirty when added
idata.flags |= InstanceData::FLAG_REFLECTION_PROBE_DIRTY;
}
- if (p_instance->cast_shadows != RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
+ if (p_instance->cast_shadows != RS::SHADOW_CASTING_SETTING_OFF) {
idata.flags |= InstanceData::FLAG_CAST_SHADOWS;
}
if (p_instance->cast_shadows == RS::SHADOW_CASTING_SETTING_SHADOWS_ONLY) {
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 9c38bf7606..53f2d96f52 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -2763,6 +2763,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
bool is_const = false;
ConstantNode::Value value;
+ value.sint = -1;
_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value);
if (!is_const || value.sint < min || value.sint > max) {
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 348d46545b..039dbc71e3 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -1942,6 +1942,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("directional_shadow_atlas_set_size", "size", "is_16bits"), &RenderingServer::directional_shadow_atlas_set_size);
BIND_ENUM_CONSTANT(SHADOW_QUALITY_HARD);
+ BIND_ENUM_CONSTANT(SHADOW_QUALITY_SOFT_VERY_LOW);
BIND_ENUM_CONSTANT(SHADOW_QUALITY_SOFT_LOW);
BIND_ENUM_CONSTANT(SHADOW_QUALITY_SOFT_MEDIUM);
BIND_ENUM_CONSTANT(SHADOW_QUALITY_SOFT_HIGH);
@@ -2793,14 +2794,14 @@ RenderingServer::RenderingServer() {
GLOBAL_DEF("rendering/shadows/directional_shadow/size", 4096);
GLOBAL_DEF("rendering/shadows/directional_shadow/size.mobile", 2048);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/size", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/size", PROPERTY_HINT_RANGE, "256,16384"));
- GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_quality", 2);
+ GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_quality", 3);
GLOBAL_DEF("rendering/shadows/directional_shadow/soft_shadow_quality.mobile", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/soft_shadow_quality", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/soft_shadow_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"));
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/directional_shadow/soft_shadow_quality", PropertyInfo(Variant::INT, "rendering/shadows/directional_shadow/soft_shadow_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"));
GLOBAL_DEF("rendering/shadows/directional_shadow/16_bits", true);
- GLOBAL_DEF("rendering/shadows/shadows/soft_shadow_quality", 2);
+ GLOBAL_DEF("rendering/shadows/shadows/soft_shadow_quality", 3);
GLOBAL_DEF("rendering/shadows/shadows/soft_shadow_quality.mobile", 0);
- ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadows/soft_shadow_quality", PropertyInfo(Variant::INT, "rendering/shadows/shadows/soft_shadow_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"));
+ ProjectSettings::get_singleton()->set_custom_property_info("rendering/shadows/shadows/soft_shadow_quality", PropertyInfo(Variant::INT, "rendering/shadows/shadows/soft_shadow_quality", PROPERTY_HINT_ENUM, "Hard (Fastest),Soft Very Low (Faster),Soft Low (Fast),Soft Medium (Average),Soft High (Slow),Soft Ultra (Slowest)"));
GLOBAL_DEF("rendering/2d/shadow_atlas/size", 2048);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index b50da66d03..b6068afcf1 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -474,6 +474,7 @@ public:
enum ShadowQuality {
SHADOW_QUALITY_HARD,
+ SHADOW_QUALITY_SOFT_VERY_LOW,
SHADOW_QUALITY_SOFT_LOW,
SHADOW_QUALITY_SOFT_MEDIUM,
SHADOW_QUALITY_SOFT_HIGH,
diff --git a/tests/test_basis.h b/tests/test_basis.h
index 11c68f9eb7..b254d9fb7f 100644
--- a/tests/test_basis.h
+++ b/tests/test_basis.h
@@ -60,27 +60,27 @@ Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
Basis ret;
switch (mode) {
case EulerXYZ:
- ret.set_euler_xyz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
break;
case EulerXZY:
- ret.set_euler_xzy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
break;
case EulerYZX:
- ret.set_euler_yzx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
break;
case EulerYXZ:
- ret.set_euler_yxz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
break;
case EulerZXY:
- ret.set_euler_zxy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
break;
case EulerZYX:
- ret.set_euler_zyx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
break;
default:
@@ -94,22 +94,22 @@ Basis EulerToBasis(RotOrder mode, const Vector3 &p_rotation) {
Vector3 BasisToEuler(RotOrder mode, const Basis &p_rotation) {
switch (mode) {
case EulerXYZ:
- return p_rotation.get_euler_xyz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
case EulerXZY:
- return p_rotation.get_euler_xzy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
case EulerYZX:
- return p_rotation.get_euler_yzx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
case EulerYXZ:
- return p_rotation.get_euler_yxz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
case EulerZXY:
- return p_rotation.get_euler_zxy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
case EulerZYX:
- return p_rotation.get_euler_zyx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
default:
// If you land here, Please integrate all rotation orders.
@@ -170,9 +170,9 @@ void test_rotation(Vector3 deg_original_euler, RotOrder rot_order) {
CHECK_MESSAGE((res.get_axis(2) - Vector3(0.0, 0.0, 1.0)).length() <= 0.1, vformat("Fail due to Z %s\n", String(res.get_axis(2))).utf8().ptr());
// Double check `to_rotation` decomposing with XYZ rotation order.
- const Vector3 euler_xyz_from_rotation = to_rotation.get_euler_xyz();
+ const Vector3 euler_xyz_from_rotation = to_rotation.get_euler(Basis::EULER_ORDER_XYZ);
Basis rotation_from_xyz_computed_euler;
- rotation_from_xyz_computed_euler.set_euler_xyz(euler_xyz_from_rotation);
+ rotation_from_xyz_computed_euler.set_euler(euler_xyz_from_rotation, Basis::EULER_ORDER_XYZ);
res = to_rotation.inverse() * rotation_from_xyz_computed_euler;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 964ac6246e..319e4d1ee0 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -318,6 +318,9 @@ File extracted from upstream release tarball:
pre-defined symbol.
- Applied the patch in `patches/pr4948-fix-clang12-opt.patch`. Upstream bugfix
from PR 4948 to fix a bug caused by Clang 12 optimizations.
+- Applied the patch in `patches/pr4819-faster-base64.patch`. This fixes a certs
+ parsing speed regression since 2.16.10 (upstream PR:
+ https://github.com/ARMmbed/mbedtls/pull/4819).
- Added 2 files `godot_core_mbedtls_platform.c` and `godot_core_mbedtls_config.h`
providing configuration for light bundling with core.
diff --git a/thirdparty/mbedtls/library/base64.c b/thirdparty/mbedtls/library/base64.c
index 692e11e3fa..b89313062b 100644
--- a/thirdparty/mbedtls/library/base64.c
+++ b/thirdparty/mbedtls/library/base64.c
@@ -66,127 +66,38 @@
#endif /* MBEDTLS_PLATFORM_C */
#endif /* MBEDTLS_SELF_TEST */
-static const unsigned char base64_enc_map[64] =
-{
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
- 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
- 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
- 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
- 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', '+', '/'
-};
-
-static const unsigned char base64_dec_map[128] =
-{
- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
- 127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
- 54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
- 127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
- 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 127, 127, 127, 127, 127
-};
-
#define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */
-/*
- * Constant flow conditional assignment to unsigned char
- */
-static void mbedtls_base64_cond_assign_uchar( unsigned char * dest, const unsigned char * const src,
- unsigned char condition )
-{
- /* MSVC has a warning about unary minus on unsigned integer types,
- * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-
- /* Generate bitmask from condition, mask will either be 0xFF or 0 */
- unsigned char mask = ( condition | -condition );
- mask >>= 7;
- mask = -mask;
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
- *dest = ( ( *src ) & mask ) | ( ( *dest ) & ~mask );
-}
-
-/*
- * Constant flow conditional assignment to uint_32
- */
-static void mbedtls_base64_cond_assign_uint32( uint32_t * dest, const uint32_t src,
- uint32_t condition )
-{
- /* MSVC has a warning about unary minus on unsigned integer types,
- * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-
- /* Generate bitmask from condition, mask will either be 0xFFFFFFFF or 0 */
- uint32_t mask = ( condition | -condition );
- mask >>= 31;
- mask = -mask;
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
- *dest = ( src & mask ) | ( ( *dest ) & ~mask );
-}
-
-/*
- * Constant flow check for equality
+/* Return 0xff if low <= c <= high, 0 otherwise.
+ *
+ * Constant flow with respect to c.
*/
-static unsigned char mbedtls_base64_eq( size_t in_a, size_t in_b )
+static unsigned char mask_of_range( unsigned char low, unsigned char high,
+ unsigned char c )
{
- size_t difference = in_a ^ in_b;
-
- /* MSVC has a warning about unary minus on unsigned integer types,
- * but this is well-defined and precisely what we want to do here. */
-#if defined(_MSC_VER)
-#pragma warning( push )
-#pragma warning( disable : 4146 )
-#endif
-
- difference |= -difference;
-
-#if defined(_MSC_VER)
-#pragma warning( pop )
-#endif
-
- /* cope with the varying size of size_t per platform */
- difference >>= ( sizeof( difference ) * 8 - 1 );
-
- return (unsigned char) ( 1 ^ difference );
+ /* low_mask is: 0 if low <= c, 0x...ff if low > c */
+ unsigned low_mask = ( (unsigned) c - low ) >> 8;
+ /* high_mask is: 0 if c <= high, 0x...ff if high > c */
+ unsigned high_mask = ( (unsigned) high - c ) >> 8;
+ return( ~( low_mask | high_mask ) & 0xff );
}
-/*
- * Constant flow lookup into table.
+/* Given a value in the range 0..63, return the corresponding Base64 digit.
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
*/
-static unsigned char mbedtls_base64_table_lookup( const unsigned char * const table,
- const size_t table_size, const size_t table_index )
+static unsigned char enc_char( unsigned char val )
{
- size_t i;
- unsigned char result = 0;
-
- for( i = 0; i < table_size; ++i )
- {
- mbedtls_base64_cond_assign_uchar( &result, &table[i], mbedtls_base64_eq( i, table_index ) );
- }
-
- return result;
+ unsigned char digit = 0;
+ /* For each range of values, if val is in that range, mask digit with
+ * the corresponding value. Since val can only be in a single range,
+ * only at most one masking will change digit. */
+ digit |= mask_of_range( 0, 25, val ) & ( 'A' + val );
+ digit |= mask_of_range( 26, 51, val ) & ( 'a' + val - 26 );
+ digit |= mask_of_range( 52, 61, val ) & ( '0' + val - 52 );
+ digit |= mask_of_range( 62, 62, val ) & '+';
+ digit |= mask_of_range( 63, 63, val ) & '/';
+ return( digit );
}
/*
@@ -229,17 +140,10 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
C2 = *src++;
C3 = *src++;
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( C1 >> 2 ) & 0x3F ) );
-
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) );
-
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F ) );
-
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( C3 & 0x3F ) );
+ *p++ = enc_char( ( C1 >> 2 ) & 0x3F );
+ *p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F );
+ *p++ = enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F );
+ *p++ = enc_char( C3 & 0x3F );
}
if( i < slen )
@@ -247,15 +151,11 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
C1 = *src++;
C2 = ( ( i + 1 ) < slen ) ? *src++ : 0;
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( C1 >> 2 ) & 0x3F ) );
-
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) );
+ *p++ = enc_char( ( C1 >> 2 ) & 0x3F );
+ *p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F );
if( ( i + 1 ) < slen )
- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
- ( ( ( C2 & 15 ) << 2 ) & 0x3F ) );
+ *p++ = enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F );
else *p++ = '=';
*p++ = '=';
@@ -267,26 +167,57 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
return( 0 );
}
+/* Given a Base64 digit, return its value.
+ * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
+ * return -1.
+ *
+ * The implementation assumes that letters are consecutive (e.g. ASCII
+ * but not EBCDIC).
+ *
+ * The implementation is constant-flow (no branch or memory access depending
+ * on the value of c) unless the compiler inlines and optimizes a specific
+ * access.
+ */
+static signed char dec_value( unsigned char c )
+{
+ unsigned char val = 0;
+ /* For each range of digits, if c is in that range, mask val with
+ * the corresponding value. Since c can only be in a single range,
+ * only at most one masking will change val. Set val to one plus
+ * the desired value so that it stays 0 if c is in none of the ranges. */
+ val |= mask_of_range( 'A', 'Z', c ) & ( c - 'A' + 0 + 1 );
+ val |= mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 );
+ val |= mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 );
+ val |= mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 );
+ val |= mask_of_range( '/', '/', c ) & ( c - '/' + 63 + 1 );
+ /* At this point, val is 0 if c is an invalid digit and v+1 if c is
+ * a digit with the value v. */
+ return( val - 1 );
+}
+
/*
* Decode a base64-formatted buffer
*/
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
const unsigned char *src, size_t slen )
{
- size_t i, n;
- uint32_t j, x;
+ size_t i; /* index in source */
+ size_t n; /* number of digits or trailing = in source */
+ uint32_t x; /* value accumulator */
+ unsigned accumulated_digits = 0;
+ unsigned equals = 0;
+ int spaces_present = 0;
unsigned char *p;
- unsigned char dec_map_lookup;
/* First pass: check for validity and get output length */
- for( i = n = j = 0; i < slen; i++ )
+ for( i = n = 0; i < slen; i++ )
{
/* Skip spaces before checking for EOL */
- x = 0;
+ spaces_present = 0;
while( i < slen && src[i] == ' ' )
{
++i;
- ++x;
+ spaces_present = 1;
}
/* Spaces at end of buffer are OK */
@@ -301,20 +232,24 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
continue;
/* Space inside a line is an error */
- if( x != 0 )
+ if( spaces_present )
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
- if( src[i] == '=' && ++j > 2 )
- return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
-
- dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), src[i] );
-
- if( src[i] > 127 || dec_map_lookup == 127 )
- return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
-
- if( dec_map_lookup < 64 && j != 0 )
+ if( src[i] > 127 )
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+ if( src[i] == '=' )
+ {
+ if( ++equals > 2 )
+ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+ }
+ else
+ {
+ if( equals != 0 )
+ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+ if( dec_value( src[i] ) < 0 )
+ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+ }
n++;
}
@@ -329,7 +264,7 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
* n = ( ( n * 6 ) + 7 ) >> 3;
*/
n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 );
- n -= j;
+ n -= equals;
if( dst == NULL || dlen < n )
{
@@ -337,22 +272,24 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
}
- for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
- {
+ equals = 0;
+ for( x = 0, p = dst; i > 0; i--, src++ )
+ {
if( *src == '\r' || *src == '\n' || *src == ' ' )
continue;
- dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), *src );
-
- mbedtls_base64_cond_assign_uint32( &j, j - 1, mbedtls_base64_eq( dec_map_lookup, 64 ) );
- x = ( x << 6 ) | ( dec_map_lookup & 0x3F );
+ x = x << 6;
+ if( *src == '=' )
+ ++equals;
+ else
+ x |= dec_value( *src );
- if( ++n == 4 )
+ if( ++accumulated_digits == 4 )
{
- n = 0;
- if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
- if( j > 1 ) *p++ = (unsigned char)( x >> 8 );
- if( j > 2 ) *p++ = (unsigned char)( x );
+ accumulated_digits = 0;
+ *p++ = (unsigned char)( x >> 16 );
+ if( equals <= 1 ) *p++ = (unsigned char)( x >> 8 );
+ if( equals <= 0 ) *p++ = (unsigned char)( x );
}
}
diff --git a/thirdparty/mbedtls/patches/pr4819-faster-base64.patch b/thirdparty/mbedtls/patches/pr4819-faster-base64.patch
new file mode 100644
index 0000000000..2f25b7b54c
--- /dev/null
+++ b/thirdparty/mbedtls/patches/pr4819-faster-base64.patch
@@ -0,0 +1,341 @@
+diff --git a/library/base64.c b/library/base64.c
+index 692e11e3fae..b89313062b6 100644
+--- a/library/base64.c
++++ b/library/base64.c
+@@ -66,127 +66,38 @@
+ #endif /* MBEDTLS_PLATFORM_C */
+ #endif /* MBEDTLS_SELF_TEST */
+
+-static const unsigned char base64_enc_map[64] =
+-{
+- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+- 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+- 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+- 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+- 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+- 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+- '8', '9', '+', '/'
+-};
+-
+-static const unsigned char base64_dec_map[128] =
+-{
+- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+- 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
+- 127, 127, 127, 62, 127, 127, 127, 63, 52, 53,
+- 54, 55, 56, 57, 58, 59, 60, 61, 127, 127,
+- 127, 64, 127, 127, 127, 0, 1, 2, 3, 4,
+- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+- 25, 127, 127, 127, 127, 127, 127, 26, 27, 28,
+- 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+- 49, 50, 51, 127, 127, 127, 127, 127
+-};
+-
+ #define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */
+
+-/*
+- * Constant flow conditional assignment to unsigned char
+- */
+-static void mbedtls_base64_cond_assign_uchar( unsigned char * dest, const unsigned char * const src,
+- unsigned char condition )
+-{
+- /* MSVC has a warning about unary minus on unsigned integer types,
+- * but this is well-defined and precisely what we want to do here. */
+-#if defined(_MSC_VER)
+-#pragma warning( push )
+-#pragma warning( disable : 4146 )
+-#endif
+-
+- /* Generate bitmask from condition, mask will either be 0xFF or 0 */
+- unsigned char mask = ( condition | -condition );
+- mask >>= 7;
+- mask = -mask;
+-
+-#if defined(_MSC_VER)
+-#pragma warning( pop )
+-#endif
+-
+- *dest = ( ( *src ) & mask ) | ( ( *dest ) & ~mask );
+-}
+-
+-/*
+- * Constant flow conditional assignment to uint_32
+- */
+-static void mbedtls_base64_cond_assign_uint32( uint32_t * dest, const uint32_t src,
+- uint32_t condition )
+-{
+- /* MSVC has a warning about unary minus on unsigned integer types,
+- * but this is well-defined and precisely what we want to do here. */
+-#if defined(_MSC_VER)
+-#pragma warning( push )
+-#pragma warning( disable : 4146 )
+-#endif
+-
+- /* Generate bitmask from condition, mask will either be 0xFFFFFFFF or 0 */
+- uint32_t mask = ( condition | -condition );
+- mask >>= 31;
+- mask = -mask;
+-
+-#if defined(_MSC_VER)
+-#pragma warning( pop )
+-#endif
+-
+- *dest = ( src & mask ) | ( ( *dest ) & ~mask );
+-}
+-
+-/*
+- * Constant flow check for equality
++/* Return 0xff if low <= c <= high, 0 otherwise.
++ *
++ * Constant flow with respect to c.
+ */
+-static unsigned char mbedtls_base64_eq( size_t in_a, size_t in_b )
++static unsigned char mask_of_range( unsigned char low, unsigned char high,
++ unsigned char c )
+ {
+- size_t difference = in_a ^ in_b;
+-
+- /* MSVC has a warning about unary minus on unsigned integer types,
+- * but this is well-defined and precisely what we want to do here. */
+-#if defined(_MSC_VER)
+-#pragma warning( push )
+-#pragma warning( disable : 4146 )
+-#endif
+-
+- difference |= -difference;
+-
+-#if defined(_MSC_VER)
+-#pragma warning( pop )
+-#endif
+-
+- /* cope with the varying size of size_t per platform */
+- difference >>= ( sizeof( difference ) * 8 - 1 );
+-
+- return (unsigned char) ( 1 ^ difference );
++ /* low_mask is: 0 if low <= c, 0x...ff if low > c */
++ unsigned low_mask = ( (unsigned) c - low ) >> 8;
++ /* high_mask is: 0 if c <= high, 0x...ff if high > c */
++ unsigned high_mask = ( (unsigned) high - c ) >> 8;
++ return( ~( low_mask | high_mask ) & 0xff );
+ }
+
+-/*
+- * Constant flow lookup into table.
++/* Given a value in the range 0..63, return the corresponding Base64 digit.
++ * The implementation assumes that letters are consecutive (e.g. ASCII
++ * but not EBCDIC).
+ */
+-static unsigned char mbedtls_base64_table_lookup( const unsigned char * const table,
+- const size_t table_size, const size_t table_index )
++static unsigned char enc_char( unsigned char val )
+ {
+- size_t i;
+- unsigned char result = 0;
+-
+- for( i = 0; i < table_size; ++i )
+- {
+- mbedtls_base64_cond_assign_uchar( &result, &table[i], mbedtls_base64_eq( i, table_index ) );
+- }
+-
+- return result;
++ unsigned char digit = 0;
++ /* For each range of values, if val is in that range, mask digit with
++ * the corresponding value. Since val can only be in a single range,
++ * only at most one masking will change digit. */
++ digit |= mask_of_range( 0, 25, val ) & ( 'A' + val );
++ digit |= mask_of_range( 26, 51, val ) & ( 'a' + val - 26 );
++ digit |= mask_of_range( 52, 61, val ) & ( '0' + val - 52 );
++ digit |= mask_of_range( 62, 62, val ) & '+';
++ digit |= mask_of_range( 63, 63, val ) & '/';
++ return( digit );
+ }
+
+ /*
+@@ -229,17 +140,10 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
+ C2 = *src++;
+ C3 = *src++;
+
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( C1 >> 2 ) & 0x3F ) );
+-
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) );
+-
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F ) );
+-
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( C3 & 0x3F ) );
++ *p++ = enc_char( ( C1 >> 2 ) & 0x3F );
++ *p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F );
++ *p++ = enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F );
++ *p++ = enc_char( C3 & 0x3F );
+ }
+
+ if( i < slen )
+@@ -247,15 +151,11 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
+ C1 = *src++;
+ C2 = ( ( i + 1 ) < slen ) ? *src++ : 0;
+
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( C1 >> 2 ) & 0x3F ) );
+-
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) );
++ *p++ = enc_char( ( C1 >> 2 ) & 0x3F );
++ *p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F );
+
+ if( ( i + 1 ) < slen )
+- *p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
+- ( ( ( C2 & 15 ) << 2 ) & 0x3F ) );
++ *p++ = enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F );
+ else *p++ = '=';
+
+ *p++ = '=';
+@@ -267,26 +167,57 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
+ return( 0 );
+ }
+
++/* Given a Base64 digit, return its value.
++ * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
++ * return -1.
++ *
++ * The implementation assumes that letters are consecutive (e.g. ASCII
++ * but not EBCDIC).
++ *
++ * The implementation is constant-flow (no branch or memory access depending
++ * on the value of c) unless the compiler inlines and optimizes a specific
++ * access.
++ */
++static signed char dec_value( unsigned char c )
++{
++ unsigned char val = 0;
++ /* For each range of digits, if c is in that range, mask val with
++ * the corresponding value. Since c can only be in a single range,
++ * only at most one masking will change val. Set val to one plus
++ * the desired value so that it stays 0 if c is in none of the ranges. */
++ val |= mask_of_range( 'A', 'Z', c ) & ( c - 'A' + 0 + 1 );
++ val |= mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 );
++ val |= mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 );
++ val |= mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 );
++ val |= mask_of_range( '/', '/', c ) & ( c - '/' + 63 + 1 );
++ /* At this point, val is 0 if c is an invalid digit and v+1 if c is
++ * a digit with the value v. */
++ return( val - 1 );
++}
++
+ /*
+ * Decode a base64-formatted buffer
+ */
+ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+ const unsigned char *src, size_t slen )
+ {
+- size_t i, n;
+- uint32_t j, x;
++ size_t i; /* index in source */
++ size_t n; /* number of digits or trailing = in source */
++ uint32_t x; /* value accumulator */
++ unsigned accumulated_digits = 0;
++ unsigned equals = 0;
++ int spaces_present = 0;
+ unsigned char *p;
+- unsigned char dec_map_lookup;
+
+ /* First pass: check for validity and get output length */
+- for( i = n = j = 0; i < slen; i++ )
++ for( i = n = 0; i < slen; i++ )
+ {
+ /* Skip spaces before checking for EOL */
+- x = 0;
++ spaces_present = 0;
+ while( i < slen && src[i] == ' ' )
+ {
+ ++i;
+- ++x;
++ spaces_present = 1;
+ }
+
+ /* Spaces at end of buffer are OK */
+@@ -301,20 +232,24 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+ continue;
+
+ /* Space inside a line is an error */
+- if( x != 0 )
++ if( spaces_present )
+ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
+- if( src[i] == '=' && ++j > 2 )
+- return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+-
+- dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), src[i] );
+-
+- if( src[i] > 127 || dec_map_lookup == 127 )
+- return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+-
+- if( dec_map_lookup < 64 && j != 0 )
++ if( src[i] > 127 )
+ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
+
++ if( src[i] == '=' )
++ {
++ if( ++equals > 2 )
++ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
++ }
++ else
++ {
++ if( equals != 0 )
++ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
++ if( dec_value( src[i] ) < 0 )
++ return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
++ }
+ n++;
+ }
+
+@@ -329,7 +264,7 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+ * n = ( ( n * 6 ) + 7 ) >> 3;
+ */
+ n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 );
+- n -= j;
++ n -= equals;
+
+ if( dst == NULL || dlen < n )
+ {
+@@ -337,22 +272,24 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
+ return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
+ }
+
+- for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ )
+- {
++ equals = 0;
++ for( x = 0, p = dst; i > 0; i--, src++ )
++ {
+ if( *src == '\r' || *src == '\n' || *src == ' ' )
+ continue;
+
+- dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), *src );
+-
+- mbedtls_base64_cond_assign_uint32( &j, j - 1, mbedtls_base64_eq( dec_map_lookup, 64 ) );
+- x = ( x << 6 ) | ( dec_map_lookup & 0x3F );
++ x = x << 6;
++ if( *src == '=' )
++ ++equals;
++ else
++ x |= dec_value( *src );
+
+- if( ++n == 4 )
++ if( ++accumulated_digits == 4 )
+ {
+- n = 0;
+- if( j > 0 ) *p++ = (unsigned char)( x >> 16 );
+- if( j > 1 ) *p++ = (unsigned char)( x >> 8 );
+- if( j > 2 ) *p++ = (unsigned char)( x );
++ accumulated_digits = 0;
++ *p++ = (unsigned char)( x >> 16 );
++ if( equals <= 1 ) *p++ = (unsigned char)( x >> 8 );
++ if( equals <= 0 ) *p++ = (unsigned char)( x );
+ }
+ }
+