summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/csg/csg.cpp6
-rw-r--r--modules/csg/csg.h16
-rw-r--r--modules/csg/csg_shape.cpp18
-rw-r--r--modules/csg/csg_shape.h6
-rw-r--r--modules/csg/editor/csg_gizmos.cpp8
-rw-r--r--modules/dds/texture_loader_dds.cpp4
-rw-r--r--modules/enet/enet_multiplayer_peer.h4
-rw-r--r--modules/freetype/SCsub3
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml158
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp4
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h2
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h8
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd9
-rw-r--r--modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd7
-rw-r--r--modules/gdscript/gdscript.cpp164
-rw-r--r--modules/gdscript/gdscript.h75
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp113
-rw-r--r--modules/gdscript/gdscript_analyzer.h2
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp12
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h44
-rw-r--r--modules/gdscript/gdscript_cache.cpp8
-rw-r--r--modules/gdscript/gdscript_cache.h4
-rw-r--r--modules/gdscript/gdscript_codegen.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp290
-rw-r--r--modules/gdscript/gdscript_compiler.h14
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp8
-rw-r--r--modules/gdscript/gdscript_editor.cpp101
-rw-r--r--modules/gdscript/gdscript_function.cpp9
-rw-r--r--modules/gdscript/gdscript_function.h3
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp6
-rw-r--r--modules/gdscript/gdscript_parser.cpp544
-rw-r--r--modules/gdscript/gdscript_parser.h57
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp2
-rw-r--r--modules/gdscript/gdscript_tokenizer.h8
-rw-r--r--modules/gdscript/gdscript_vm.cpp84
-rw-r--r--modules/gdscript/gdscript_warning.cpp16
-rw-r--r--modules/gdscript/gdscript_warning.h10
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp8
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp16
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp58
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h6
-rw-r--r--modules/gdscript/language_server/lsp.hpp12
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp6
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd49
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out19
-rw-r--r--modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/if_after_lambda.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_bind_unused.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.gd43
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_dictionary.out15
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd26
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd16
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out7
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp46
-rw-r--r--modules/glslang/register_types.cpp34
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml69
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml2
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp10
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h4
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.cpp6
-rw-r--r--modules/gltf/editor/editor_scene_importer_fbx.h4
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.cpp3
-rw-r--r--modules/gltf/editor/editor_scene_importer_gltf.h5
-rw-r--r--modules/gltf/gltf_accessor.h1
-rw-r--r--modules/gltf/gltf_animation.cpp2
-rw-r--r--modules/gltf/gltf_animation.h4
-rw-r--r--modules/gltf/gltf_document.cpp201
-rw-r--r--modules/gltf/gltf_document.h25
-rw-r--r--modules/gltf/gltf_document_extension.cpp100
-rw-r--r--modules/gltf/gltf_document_extension.h38
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.cpp45
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.h8
-rw-r--r--modules/gltf/gltf_skeleton.cpp2
-rw-r--r--modules/gltf/gltf_skeleton.h8
-rw-r--r--modules/gltf/gltf_skin.cpp12
-rw-r--r--modules/gltf/gltf_skin.h4
-rw-r--r--modules/gltf/gltf_state.cpp13
-rw-r--r--modules/gltf/gltf_state.h29
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml17
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp7
-rw-r--r--modules/gridmap/grid_map.cpp109
-rw-r--r--modules/gridmap/grid_map.h29
-rw-r--r--modules/jpg/SCsub1
-rw-r--r--modules/jpg/image_loader_jpegd.cpp56
-rw-r--r--modules/jsonrpc/jsonrpc.h2
-rw-r--r--modules/lightmapper_rd/SCsub4
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp51
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h39
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl3
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl86
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.cpp4
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h1
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp4
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp4
-rw-r--r--modules/minimp3/resource_importer_mp3.h4
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py2
-rw-r--r--modules/mono/class_db_api_json.cpp2
-rw-r--r--modules/mono/csharp_script.cpp97
-rw-r--r--modules/mono/csharp_script.h31
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj2
-rw-r--r--modules/mono/editor/bindings_generator.cpp149
-rw-r--r--modules/mono/editor/bindings_generator.h21
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs16
-rw-r--r--modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs15
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs43
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs10
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs47
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs18
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs28
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs28
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj2
-rw-r--r--modules/mono/glue/base_object_glue.cpp4
-rw-r--r--modules/mono/glue/gd_glue.cpp22
-rw-r--r--modules/mono/glue/nodepath_glue.cpp5
-rw-r--r--modules/mono/managed_callable.cpp5
-rw-r--r--modules/mono/managed_callable.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp4
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp16
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp20
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp10
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h1
-rw-r--r--modules/mono/signal_awaiter_utils.cpp4
-rw-r--r--modules/mono/utils/path_utils.cpp12
-rw-r--r--modules/mono/utils/string_utils.cpp4
-rw-r--r--modules/navigation/godot_navigation_server.cpp111
-rw-r--r--modules/navigation/godot_navigation_server.h23
-rw-r--r--modules/navigation/nav_map.cpp28
-rw-r--r--modules/navigation/nav_map.h14
-rw-r--r--modules/navigation/nav_region.cpp8
-rw-r--r--modules/navigation/nav_region.h14
-rw-r--r--modules/navigation/nav_utils.h12
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp67
-rw-r--r--modules/navigation/rvo_agent.cpp3
-rw-r--r--modules/noise/editor/noise_editor_plugin.cpp2
-rw-r--r--modules/noise/fastnoise_lite.cpp53
-rw-r--r--modules/noise/fastnoise_lite.h3
-rw-r--r--modules/noise/noise_texture.cpp4
-rw-r--r--modules/openxr/SCsub2
-rw-r--r--modules/openxr/action_map/openxr_action_map.cpp65
-rw-r--r--modules/openxr/action_map/openxr_defs.cpp158
-rw-r--r--modules/openxr/action_map/openxr_defs.h21
-rw-r--r--modules/openxr/editor/openxr_action_editor.cpp3
-rw-r--r--modules/openxr/editor/openxr_action_map_editor.cpp1
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper.h6
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp67
-rw-r--r--modules/openxr/extensions/openxr_htc_vive_tracker_extension.h52
-rw-r--r--modules/openxr/extensions/openxr_vulkan_extension.cpp2
-rw-r--r--modules/openxr/openxr_api.cpp35
-rw-r--r--modules/openxr/openxr_api.h12
-rw-r--r--modules/openxr/openxr_interface.cpp21
-rw-r--r--modules/raycast/lightmap_raycaster.cpp10
-rw-r--r--modules/raycast/lightmap_raycaster.h6
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp6
-rw-r--r--modules/raycast/raycast_occlusion_cull.h16
-rw-r--r--modules/raycast/static_raycaster.cpp10
-rw-r--r--modules/raycast/static_raycaster.h4
-rw-r--r--modules/regex/regex.cpp8
-rw-r--r--modules/regex/regex.h4
-rw-r--r--modules/text_server_adv/SCsub26
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct36
-rw-r--r--modules/text_server_adv/text_server_adv.cpp1088
-rw-r--r--modules/text_server_adv/text_server_adv.h86
-rw-r--r--modules/text_server_fb/gdextension_build/SConstruct10
-rw-r--r--modules/text_server_fb/text_server_fb.cpp465
-rw-r--r--modules/text_server_fb/text_server_fb.h60
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp33
-rw-r--r--modules/tinyexr/image_saver_tinyexr.h1
-rw-r--r--modules/tinyexr/register_types.cpp1
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp197
-rw-r--r--modules/visual_script/editor/visual_script_editor.h22
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp39
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h10
-rw-r--r--modules/visual_script/visual_script.cpp167
-rw-r--r--modules/visual_script/visual_script.h38
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp6
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp12
-rw-r--r--modules/visual_script/visual_script_nodes.cpp10
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp17
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp4
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h4
-rw-r--r--modules/webp/image_loader_webp.cpp181
-rw-r--r--modules/webp/image_loader_webp.h4
-rw-r--r--modules/webp/register_types.cpp10
-rw-r--r--modules/webp/resource_saver_webp.cpp90
-rw-r--r--modules/webp/resource_saver_webp.h49
-rw-r--r--modules/webp/webp_common.cpp190
-rw-r--r--modules/webp/webp_common.h45
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml7
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml5
-rw-r--r--modules/webrtc/webrtc_data_channel.h2
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp36
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h2
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h2
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp2
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h2
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h2
-rw-r--r--modules/websocket/wsl_client.cpp2
-rw-r--r--modules/websocket/wsl_server.cpp2
247 files changed, 5335 insertions, 2587 deletions
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 177014e5a7..93533e1690 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -211,7 +211,7 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
int ic = p_flip_faces.size();
const bool *ri = p_flip_faces.ptr();
- Map<Ref<Material>, int> material_map;
+ HashMap<Ref<Material>, int> material_map;
faces.resize(p_vertices.size() / 3);
@@ -242,10 +242,10 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
if (mc == vc / 3) {
Ref<Material> mat = rm[i];
if (mat.is_valid()) {
- const Map<Ref<Material>, int>::Element *E = material_map.find(mat);
+ HashMap<Ref<Material>, int>::ConstIterator E = material_map.find(mat);
if (E) {
- f.material = E->get();
+ f.material = E->value;
} else {
f.material = material_map.size();
material_map[mat] = f.material;
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 9ff7b13a44..738e3d68ea 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -38,8 +38,8 @@
#include "core/math/vector3.h"
#include "core/object/ref_counted.h"
#include "core/templates/list.h"
-#include "core/templates/map.h"
#include "core/templates/oa_hash_map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "scene/resources/material.h"
@@ -130,17 +130,17 @@ struct CSGBrushOperation {
struct VertexKeyHash {
static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) {
- uint32_t h = hash_djb2_one_32(p_vk.x);
- h = hash_djb2_one_32(p_vk.y, h);
- h = hash_djb2_one_32(p_vk.z, h);
+ uint32_t h = hash_murmur3_one_32(p_vk.x);
+ h = hash_murmur3_one_32(p_vk.y, h);
+ h = hash_murmur3_one_32(p_vk.z, h);
return h;
}
};
Vector<Vector3> points;
Vector<Face> faces;
- Map<Ref<Material>, int> materials;
- Map<Vector3, int> vertex_map;
+ HashMap<Ref<Material>, int> materials;
+ HashMap<Vector3, int> vertex_map;
OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
float vertex_snap = 0.0;
@@ -184,8 +184,8 @@ struct CSGBrushOperation {
};
struct Build2DFaceCollection {
- Map<int, Build2DFaces> build2DFacesA;
- Map<int, Build2DFaces> build2DFacesB;
+ HashMap<int, Build2DFaces> build2DFacesA;
+ HashMap<int, Build2DFaces> build2DFacesB;
};
void update_faces(const CSGBrush &p_brush_a, const int p_face_idx_a, const CSGBrush &p_brush_b, const int p_face_idx_b, Build2DFaceCollection &p_collection, float p_vertex_snap);
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index a5b3db89d9..0f09eb2020 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -638,7 +638,7 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
ADD_GROUP("Collision", "collision_");
@@ -1052,7 +1052,7 @@ void CSGSphere3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere3D::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001,suffix:m"), "set_radius", "get_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1225,7 +1225,7 @@ void CSGBox3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size", PROPERTY_HINT_NONE, "suffix:m"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
@@ -1405,8 +1405,8 @@ void CSGCylinder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -1630,8 +1630,8 @@ void CSGTorus3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp,suffix:m"), "set_outer_radius", "get_outer_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
@@ -2135,7 +2135,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp,suffix:m"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spin_degrees", PROPERTY_HINT_RANGE, "1,360,0.1"), "set_spin_degrees", "get_spin_degrees");
ADD_PROPERTY(PropertyInfo(Variant::INT, "spin_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_spin_sides", "get_spin_sides");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "path_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Path3D"), "set_path_node", "get_path_node");
@@ -2145,7 +2145,7 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_local"), "set_path_local", "is_path_local");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_continuous_u"), "set_path_continuous_u", "is_path_continuous_u");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater"), "set_path_u_distance", "get_path_u_distance");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_u_distance", PROPERTY_HINT_RANGE, "0.0,10.0,0.01,or_greater,suffix:m"), "set_path_u_distance", "get_path_u_distance");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "path_joined"), "set_path_joined", "is_path_joined");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 0eaf5c3727..0b49dc4609 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -74,9 +74,9 @@ private:
struct Vector3Hasher {
_ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
- uint32_t h = hash_djb2_one_float(p_vec3.x);
- h = hash_djb2_one_float(p_vec3.y, h);
- h = hash_djb2_one_float(p_vec3.z, h);
+ uint32_t h = hash_murmur3_one_float(p_vec3.x);
+ h = hash_murmur3_one_float(p_vec3.y, h);
+ h = hash_murmur3_one_float(p_vec3.z, h);
return h;
}
};
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index 4d972e46c6..6442ff71fc 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -349,9 +349,11 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (cs->is_root_shape()) {
Array csg_meshes = cs->get_meshes();
- Ref<Mesh> csg_mesh = csg_meshes[1];
- if (csg_mesh.is_valid()) {
- p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
+ if (csg_meshes.size() == 2) {
+ Ref<Mesh> csg_mesh = csg_meshes[1];
+ if (csg_mesh.is_valid()) {
+ p_gizmo->add_collision_triangles(csg_mesh->generate_triangle_mesh());
+ }
}
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 2c0e604e66..eb5614338b 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -409,9 +409,7 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
}
Ref<Image> img = memnew(Image(width, height, mipmaps - 1, info.format, src_data));
-
- Ref<ImageTexture> texture = memnew(ImageTexture);
- texture->create_from_image(img);
+ Ref<ImageTexture> texture = ImageTexture::create_from_image(img);
if (r_error) {
*r_error = OK;
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index e7b61169fb..18eca18e51 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -70,8 +70,8 @@ private:
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
- Map<int, Ref<ENetConnection>> hosts;
- Map<int, Ref<ENetPacketPeer>> peers;
+ HashMap<int, Ref<ENetConnection>> hosts;
+ HashMap<int, Ref<ENetPacketPeer>> peers;
struct Packet {
ENetPacket *packet = nullptr;
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index d23c4b637c..4b2ea6faa5 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -49,6 +49,7 @@ if env["builtin_freetype"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -87,7 +88,7 @@ if env["builtin_freetype"]:
# Also needed in main env for scene/
env.Prepend(CPPPATH=[thirdparty_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG"])
+ env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", "FT_CONFIG_OPTION_SYSTEM_ZLIB"])
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 70151c4d21..e995cce651 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -257,4 +257,162 @@
[b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead.
</constant>
</constants>
+ <annotations>
+ <annotation name="@export">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_category">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_color_no_alpha">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_dir">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_enum" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="names" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_exp_easing" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="hints" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_file" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="names" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_navigation">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_physics">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_2d_render">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_navigation">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_physics">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_flags_3d_render">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_global_dir">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_global_file" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="filter" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_group">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="prefix" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_multiline">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_node_path" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="type" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_placeholder">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_range" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="min" type="float" />
+ <argument index="1" name="max" type="float" />
+ <argument index="2" name="step" type="float" default="1.0" />
+ <argument index="3" name="extra_hints" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@export_subgroup">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="prefix" type="String" default="&quot;&quot;" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@icon">
+ <return type="void" />
+ <argument index="0" name="icon_path" type="String" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@onready">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@rpc" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="mode" type="String" default="&quot;&quot;" />
+ <argument index="1" name="sync" type="String" default="&quot;&quot;" />
+ <argument index="2" name="transfer_mode" type="String" default="&quot;&quot;" />
+ <argument index="3" name="transfer_channel" type="int" default="0" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@tool">
+ <return type="void" />
+ <description>
+ </description>
+ </annotation>
+ <annotation name="@warning_ignore" qualifiers="vararg">
+ <return type="void" />
+ <argument index="0" name="warning" type="String" />
+ <description>
+ </description>
+ </annotation>
+ </annotations>
</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 191568661d..b86e9b386d 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -387,9 +387,9 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
in_member_variable = false;
}
- if (!in_node_path && in_region == -1 && str[j] == '$') {
+ if (!in_node_path && in_region == -1 && (str[j] == '$' || str[j] == '%')) {
in_node_path = true;
- } else if (in_region != -1 || (is_a_symbol && str[j] != '/')) {
+ } else if (in_region != -1 || (is_a_symbol && str[j] != '/' && str[j] != '%')) {
in_node_path = false;
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 1ae0d72896..92764e3891 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -45,7 +45,7 @@ private:
bool line_only = false;
};
Vector<ColorRegion> color_regions;
- Map<int, int> color_region_cache;
+ HashMap<int, int> color_region_cache;
HashMap<StringName, Color> keywords;
HashMap<StringName, Color> member_keywords;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index e7b40aa367..969a50f48c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "editor/editor_translation_parser.h"
#include "modules/gdscript/gdscript_parser.h"
@@ -44,9 +44,9 @@ class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlug
// List of patterns used for extracting translation strings.
StringName tr_func = "tr";
StringName trn_func = "tr_n";
- Set<StringName> assignment_patterns;
- Set<StringName> first_arg_patterns;
- Set<StringName> second_arg_patterns;
+ HashSet<StringName> assignment_patterns;
+ HashSet<StringName> first_arg_patterns;
+ HashSet<StringName> second_arg_patterns;
// FileDialog patterns.
StringName fd_add_filter = "add_filter";
StringName fd_set_filter = "set_filters";
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
new file mode 100644
index 0000000000..556afe994b
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/basic_import_script.gd
@@ -0,0 +1,9 @@
+# meta-description: Basic import script template
+@tool
+extends EditorScenePostImport
+
+
+# Called by the editor when a scene has this script set as the import script in the import tab.
+func _post_import(scene: Node) -> Object:
+ # Modify the contents of the scene upon import.
+ return scene # Return the modified root node when you're done.
diff --git a/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
new file mode 100644
index 0000000000..875afb4fc0
--- /dev/null
+++ b/modules/gdscript/editor/script_templates/EditorScenePostImport/no_comments.gd
@@ -0,0 +1,7 @@
+# meta-description: Basic import script template (no comments)
+@tool
+extends EditorScenePostImport
+
+
+func _post_import(scene: Node) -> Object:
+ return scene
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 1b4711804c..e74314389d 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -62,7 +62,7 @@ GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const {
bool ok;
- int v = ClassDB::get_integer_constant(name, p_name, &ok);
+ int64_t v = ClassDB::get_integer_constant(name, p_name, &ok);
if (ok) {
r_ret = v;
@@ -128,6 +128,7 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
return;
}
}
+ ERR_FAIL_NULL(p_script->implicit_initializer);
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
}
@@ -340,14 +341,14 @@ bool GDScript::has_method(const StringName &p_method) const {
}
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
- const Map<StringName, GDScriptFunction *>::Element *E = member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
return MethodInfo();
}
- GDScriptFunction *func = E->get();
+ GDScriptFunction *func = E->value;
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E->key;
for (int i = 0; i < func->get_argument_count(); i++) {
mi.arguments.push_back(func->get_argument_type(i));
}
@@ -359,9 +360,9 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const {
bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
- const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property);
+ HashMap<StringName, Variant>::ConstIterator E = member_default_values_cache.find(p_property);
if (E) {
- r_value = E->get();
+ r_value = E->value;
return true;
}
@@ -427,7 +428,7 @@ void GDScript::set_source_code(const String &p_code) {
}
#ifdef TOOLS_ENABLED
-void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) {
+void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
if (base_cache.is_valid()) {
base_cache->_update_exports_values(values, propnames);
}
@@ -535,6 +536,9 @@ void GDScript::_update_doc() {
List<PropertyInfo> props;
_get_script_property_list(&props, false);
for (int i = 0; i < props.size(); i++) {
+ if (props[i].usage & PROPERTY_USAGE_CATEGORY || props[i].usage & PROPERTY_USAGE_GROUP || props[i].usage & PROPERTY_USAGE_SUBGROUP) {
+ continue;
+ }
ScriptMemberInfo scr_member_info;
scr_member_info.propinfo = props[i];
scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
@@ -759,13 +763,13 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
if ((changed || p_instance_to_update) && placeholders.size()) { //hm :(
// update placeholders if any
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
if (changed) {
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ for (PlaceHolderScriptInstance *E : placeholders) {
+ E->update(propnames, values);
}
} else {
p_instance_to_update->update(propnames, values);
@@ -788,10 +792,10 @@ void GDScript::update_exports() {
return;
}
- Set<ObjectID> copy = inheriters_cache; //might get modified
+ HashSet<ObjectID> copy = inheriters_cache; //might get modified
- for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) {
- Object *id = ObjectDB::get_instance(E->get());
+ for (const ObjectID &E : copy) {
+ Object *id = ObjectDB::get_instance(E);
GDScript *s = Object::cast_to<GDScript>(id);
if (!s) {
continue;
@@ -929,7 +933,7 @@ ScriptLanguage *GDScript::get_language() const {
return GDScriptLanguage::get_singleton();
}
-void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
+void GDScript::get_constants(HashMap<StringName, Variant> *p_constants) {
if (p_constants) {
for (const KeyValue<StringName, Variant> &E : constants) {
(*p_constants)[E.key] = E.value;
@@ -937,10 +941,10 @@ void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
}
}
-void GDScript::get_members(Set<StringName> *p_members) {
+void GDScript::get_members(HashSet<StringName> *p_members) {
if (p_members) {
- for (Set<StringName>::Element *E = members.front(); E; E = E->next()) {
- p_members->insert(E->get());
+ for (const StringName &E : members) {
+ p_members->insert(E);
}
}
}
@@ -952,11 +956,11 @@ const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const {
Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
- Map<StringName, GDScriptFunction *>::Element *E = top->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method);
if (E) {
- ERR_FAIL_COND_V_MSG(!E->get()->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script.");
+ ERR_FAIL_COND_V_MSG(!E->value->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script.");
- return E->get()->call(nullptr, p_args, p_argcount, r_error);
+ return E->value->call(nullptr, p_args, p_argcount, r_error);
}
top = top->_base;
}
@@ -971,17 +975,17 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const {
const GDScript *top = this;
while (top) {
{
- const Map<StringName, Variant>::Element *E = top->constants.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
}
{
- const Map<StringName, Ref<GDScript>>::Element *E = subclasses.find(p_name);
+ HashMap<StringName, Ref<GDScript>>::ConstIterator E = subclasses.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
}
@@ -1049,7 +1053,7 @@ Error GDScript::load_source_code(const String &p_path) {
w[len] = 0;
String s;
- if (s.parse_utf8((const char *)w)) {
+ if (s.parse_utf8((const char *)w) != OK) {
ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
@@ -1061,7 +1065,7 @@ Error GDScript::load_source_code(const String &p_path) {
return OK;
}
-const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const {
+const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const {
return member_functions;
}
@@ -1209,7 +1213,7 @@ void GDScript::_init_rpc_methods_properties() {
}
GDScript *cscript = this;
- Map<StringName, Ref<GDScript>>::Element *sub_E = subclasses.front();
+ HashMap<StringName, Ref<GDScript>>::Iterator sub_E = subclasses.begin();
while (cscript) {
// RPC Methods
for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
@@ -1223,11 +1227,11 @@ void GDScript::_init_rpc_methods_properties() {
}
if (cscript != this) {
- sub_E = sub_E->next();
+ ++sub_E;
}
if (sub_E) {
- cscript = sub_E->get().ptr();
+ cscript = sub_E->value.ptr();
} else {
cscript = nullptr;
}
@@ -1253,6 +1257,14 @@ GDScript::~GDScript() {
memdelete(E.value);
}
+ if (implicit_initializer) {
+ memdelete(implicit_initializer);
+ }
+
+ if (implicit_ready) {
+ memdelete(implicit_ready);
+ }
+
if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
GDScriptCache::remove_script(get_path());
}
@@ -1282,9 +1294,9 @@ GDScript::~GDScript() {
bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
//member
{
- const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name);
+ HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
- const GDScript::MemberInfo *member = &E->get();
+ const GDScript::MemberInfo *member = &E->value;
if (member->setter) {
const Variant *val = &p_value;
Callable::CallError err;
@@ -1325,13 +1337,13 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
GDScript *sptr = script.ptr();
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set);
if (E) {
Variant name = p_name;
const Variant *args[2] = { &name, &p_value };
Callable::CallError err;
- Variant ret = E->get()->call(this, (const Variant **)args, 2, err);
+ Variant ret = E->value->call(this, (const Variant **)args, 2, err);
if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) {
return true;
}
@@ -1346,16 +1358,16 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
const GDScript *sptr = script.ptr();
while (sptr) {
{
- const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name);
+ HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name);
if (E) {
- if (E->get().getter) {
+ if (E->value.getter) {
Callable::CallError err;
- r_ret = const_cast<GDScriptInstance *>(this)->callp(E->get().getter, nullptr, 0, err);
+ r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err);
if (err.error == Callable::CallError::CALL_OK) {
return true;
}
}
- r_ret = members[E->get().index];
+ r_ret = members[E->value.index];
return true; //index found
}
}
@@ -1363,9 +1375,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
{
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, Variant>::Element *E = sl->constants.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = sl->constants.find(p_name);
if (E) {
- r_ret = E->get();
+ r_ret = E->value;
return true; //index found
}
sl = sl->_base;
@@ -1376,9 +1388,9 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
// Signals.
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, Vector<StringName>>::Element *E = sl->_signals.find(p_name);
+ HashMap<StringName, Vector<StringName>>::ConstIterator E = sl->_signals.find(p_name);
if (E) {
- r_ret = Signal(this->owner, E->key());
+ r_ret = Signal(this->owner, E->key);
return true; //index found
}
sl = sl->_base;
@@ -1389,14 +1401,14 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
// Methods.
const GDScript *sl = sptr;
while (sl) {
- const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sl->member_functions.find(p_name);
if (E) {
Multiplayer::RPCConfig config;
config.name = p_name;
if (sptr->rpc_functions.find(config) != -1) {
- r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key())));
+ r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key)));
} else {
- r_ret = Callable(this->owner, E->key());
+ r_ret = Callable(this->owner, E->key);
}
return true; //index found
}
@@ -1405,13 +1417,13 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
{
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get);
if (E) {
Variant name = p_name;
const Variant *args[1] = { &name };
Callable::CallError err;
- Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
+ Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err);
if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) {
r_ret = ret;
return true;
@@ -1449,10 +1461,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
List<PropertyInfo> props;
while (sptr) {
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list);
if (E) {
Callable::CallError err;
- Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
+ Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err);
if (err.error == Callable::CallError::CALL_OK) {
ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries.");
@@ -1475,6 +1487,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
if (d.has("usage")) {
pinfo.usage = d["usage"];
}
+ if (d.has("class_name")) {
+ pinfo.class_name = d["class_name"];
+ }
props.push_back(pinfo);
}
@@ -1512,7 +1527,6 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
MethodInfo mi;
mi.name = E.key;
- mi.flags |= METHOD_FLAG_FROM_SCRIPT;
for (int i = 0; i < E.value->get_argument_count(); i++) {
mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
}
@@ -1525,7 +1539,7 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
bool GDScriptInstance::has_method(const StringName &p_method) const {
const GDScript *sptr = script.ptr();
while (sptr) {
- const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method);
if (E) {
return true;
}
@@ -1537,10 +1551,22 @@ bool GDScriptInstance::has_method(const StringName &p_method) const {
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
+ if (unlikely(p_method == SNAME("_ready"))) {
+ // Call implicit ready first, including for the super classes.
+ while (sptr) {
+ if (sptr->implicit_ready) {
+ sptr->implicit_ready->call(this, nullptr, 0, r_error);
+ }
+ sptr = sptr->_base;
+ }
+
+ // Reset this back for the regular call.
+ sptr = script.ptr();
+ }
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method);
if (E) {
- return E->get()->call(this, p_args, p_argcount, r_error);
+ return E->value->call(this, p_args, p_argcount, r_error);
}
sptr = sptr->_base;
}
@@ -1555,10 +1581,10 @@ void GDScriptInstance::notification(int p_notification) {
GDScript *sptr = script.ptr();
while (sptr) {
- Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
+ HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification);
if (E) {
Callable::CallError err;
- E->get()->call(this, args, 1, err);
+ E->value->call(this, args, 1, err);
if (err.error != Callable::CallError::CALL_OK) {
//print error about notification call
}
@@ -1882,7 +1908,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//when someone asks you why dynamically typed languages are easier to write....
- Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> to_reload;
+ HashMap<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> to_reload;
//as scripts are going to be reloaded, must proceed without locking here
@@ -1895,11 +1921,11 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
continue;
}
- to_reload.insert(script, Map<ObjectID, List<Pair<StringName, Variant>>>());
+ to_reload.insert(script, HashMap<ObjectID, List<Pair<StringName, Variant>>>());
if (!p_soft_reload) {
//save state and remove script from instances
- Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
+ HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
while (script->instances.front()) {
Object *obj = script->instances.front()->get();
@@ -1916,7 +1942,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#ifdef TOOLS_ENABLED
while (script->placeholders.size()) {
- Object *obj = script->placeholders.front()->get()->get_owner();
+ Object *obj = (*script->placeholders.begin())->get_owner();
//save instance info
if (obj->get_script_instance()) {
@@ -1926,7 +1952,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
obj->set_script(Variant());
} else {
// no instance found. Let's remove it so we don't loop forever
- script->placeholders.erase(script->placeholders.front()->get());
+ script->placeholders.erase(*script->placeholders.begin());
}
}
@@ -1938,7 +1964,7 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
}
}
- for (KeyValue<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
+ for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
Ref<GDScript> scr = E.key;
scr->reload(p_soft_reload);
@@ -2232,9 +2258,13 @@ GDScriptLanguage::GDScriptLanguage() {
GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
- String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
- bool default_enabled = !warning.begins_with("unsafe_");
- GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled);
+ GDScriptWarning::Code code = (GDScriptWarning::Code)i;
+ Variant default_enabled = GDScriptWarning::get_default_value(code);
+ String path = GDScriptWarning::get_settings_path_from_code(code);
+ GLOBAL_DEF(path, default_enabled);
+
+ PropertyInfo property_info = GDScriptWarning::get_property_info(code);
+ ProjectSettings::get_singleton()->set_custom_property_info(path, property_info);
}
#endif // DEBUG_ENABLED
}
@@ -2277,13 +2307,13 @@ void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const
}
Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_name) {
- Map<String, ObjectID>::Element *orphan_subclass_element = orphan_subclasses.find(p_qualified_name);
+ HashMap<String, ObjectID>::Iterator orphan_subclass_element = orphan_subclasses.find(p_qualified_name);
if (!orphan_subclass_element) {
return Ref<GDScript>();
}
- ObjectID orphan_subclass = orphan_subclass_element->get();
+ ObjectID orphan_subclass = orphan_subclass_element->value;
Object *obj = ObjectDB::get_instance(orphan_subclass);
- orphan_subclasses.erase(orphan_subclass_element);
+ orphan_subclasses.remove(orphan_subclass_element);
if (!obj) {
return Ref<GDScript>();
}
@@ -2372,7 +2402,7 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const Ref<Resource
}
if (ScriptServer::is_reload_scripts_on_save_enabled()) {
- GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, false);
+ GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true);
}
return OK;
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index a20f3b2fca..e9a206f48b 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -37,6 +37,7 @@
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/object/script_language.h"
+#include "core/templates/rb_set.h"
#include "gdscript_function.h"
class GDScriptNativeClass : public RefCounted {
@@ -80,48 +81,49 @@ class GDScript : public Script {
GDScript *_base = nullptr; //fast pointer access
GDScript *_owner = nullptr; //for subclasses
- Set<StringName> members; //members are just indices to the instantiated script.
- Map<StringName, Variant> constants;
- Map<StringName, GDScriptFunction *> member_functions;
- Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
- Map<StringName, Ref<GDScript>> subclasses;
- Map<StringName, Vector<StringName>> _signals;
+ HashSet<StringName> members; //members are just indices to the instantiated script.
+ HashMap<StringName, Variant> constants;
+ HashMap<StringName, GDScriptFunction *> member_functions;
+ HashMap<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
+ HashMap<StringName, Ref<GDScript>> subclasses;
+ HashMap<StringName, Vector<StringName>> _signals;
Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
- Map<StringName, int> member_lines;
- Map<StringName, Variant> member_default_values;
+ HashMap<StringName, int> member_lines;
+ HashMap<StringName, Variant> member_default_values;
List<PropertyInfo> members_cache;
- Map<StringName, Variant> member_default_values_cache;
+ HashMap<StringName, Variant> member_default_values_cache;
Ref<GDScript> base_cache;
- Set<ObjectID> inheriters_cache;
+ HashSet<ObjectID> inheriters_cache;
bool source_changed_cache = false;
bool placeholder_fallback_enabled = false;
- void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
DocData::ClassDoc doc;
Vector<DocData::ClassDoc> docs;
String doc_brief_description;
String doc_description;
Vector<DocData::TutorialDoc> doc_tutorials;
- Map<String, String> doc_functions;
- Map<String, String> doc_variables;
- Map<String, String> doc_constants;
- Map<String, String> doc_signals;
- Map<String, DocData::EnumDoc> doc_enums;
+ HashMap<String, String> doc_functions;
+ HashMap<String, String> doc_variables;
+ HashMap<String, String> doc_constants;
+ HashMap<String, String> doc_signals;
+ HashMap<String, DocData::EnumDoc> doc_enums;
void _clear_doc();
void _update_doc();
void _add_doc(const DocData::ClassDoc &p_inner_class);
#endif
- Map<StringName, PropertyInfo> member_info;
+ HashMap<StringName, PropertyInfo> member_info;
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
+ GDScriptFunction *implicit_ready = nullptr;
int subclass_count = 0;
- Set<Object *> instances;
+ RBSet<Object *> instances;
//exported members
String source;
String path;
@@ -139,14 +141,14 @@ class GDScript : public Script {
String _get_debug_path() const;
#ifdef TOOLS_ENABLED
- Set<PlaceHolderScriptInstance *> placeholders;
+ HashSet<PlaceHolderScriptInstance *> placeholders;
//void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
#ifdef DEBUG_ENABLED
- Map<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state;
+ HashMap<ObjectID, List<Pair<StringName, Variant>>> pending_reload_state;
#endif
@@ -176,14 +178,14 @@ public:
bool inherits_script(const Ref<Script> &p_script) const override;
- const Map<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
- const Map<StringName, Variant> &get_constants() const { return constants; }
- const Set<StringName> &get_members() const { return members; }
+ const HashMap<StringName, Ref<GDScript>> &get_subclasses() const { return subclasses; }
+ const HashMap<StringName, Variant> &get_constants() const { return constants; }
+ const HashSet<StringName> &get_members() const { return members; }
const GDScriptDataType &get_member_type(const StringName &p_member) const {
CRASH_COND(!member_indices.has(p_member));
return member_indices[p_member].data_type;
}
- const Map<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
+ const HashMap<StringName, GDScriptFunction *> &get_member_functions() const { return member_functions; }
const Ref<GDScriptNativeClass> &get_native() const { return native; }
const String &get_script_class_name() const { return name; }
@@ -193,8 +195,8 @@ public:
bool is_tool() const override { return tool; }
Ref<GDScript> get_base() const;
- const Map<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; }
- const Map<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only
+ const HashMap<StringName, MemberInfo> &debug_get_member_indices() const { return member_indices; }
+ const HashMap<StringName, GDScriptFunction *> &debug_get_member_functions() const; //this is debug only
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -245,8 +247,8 @@ public:
return -1;
}
- virtual void get_constants(Map<StringName, Variant> *p_constants) override;
- virtual void get_members(Set<StringName> *p_members) override;
+ virtual void get_constants(HashMap<StringName, Variant> *p_constants) override;
+ virtual void get_members(HashSet<StringName> *p_members) override;
virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
@@ -270,7 +272,7 @@ class GDScriptInstance : public ScriptInstance {
Object *owner = nullptr;
Ref<GDScript> script;
#ifdef DEBUG_ENABLED
- Map<StringName, int> member_indices_cache; //used only for hot script reloading
+ HashMap<StringName, int> member_indices_cache; //used only for hot script reloading
#endif
Vector<Variant> members;
bool base_ref_counted;
@@ -315,8 +317,8 @@ class GDScriptLanguage : public ScriptLanguage {
Variant *_global_array = nullptr;
Vector<Variant> global_array;
- Map<StringName, int> globals;
- Map<StringName, Variant> named_globals;
+ HashMap<StringName, int> globals;
+ HashMap<StringName, Variant> named_globals;
struct CallLevel {
Variant *stack = nullptr;
@@ -348,7 +350,7 @@ class GDScriptLanguage : public ScriptLanguage {
bool profiling;
uint64_t script_frame_time;
- Map<String, ObjectID> orphan_subclasses;
+ HashMap<String, ObjectID> orphan_subclasses;
public:
int calls;
@@ -367,7 +369,7 @@ public:
if (_debug_call_stack_pos >= _debug_max_call_stack) {
//stack overflow
- _debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
+ _debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -427,8 +429,8 @@ public:
_FORCE_INLINE_ int get_global_array_size() const { return global_array.size(); }
_FORCE_INLINE_ Variant *get_global_array() { return _global_array; }
- _FORCE_INLINE_ const Map<StringName, int> &get_global_map() const { return globals; }
- _FORCE_INLINE_ const Map<StringName, Variant> &get_named_globals_map() const { return named_globals; }
+ _FORCE_INLINE_ const HashMap<StringName, int> &get_global_map() const { return globals; }
+ _FORCE_INLINE_ const HashMap<StringName, Variant> &get_named_globals_map() const { return named_globals; }
_FORCE_INLINE_ static GDScriptLanguage *get_singleton() { return singleton; }
@@ -449,7 +451,7 @@ public:
virtual bool is_using_templates() override;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override;
virtual Script *create_script() const override;
virtual bool has_named_classes() const override;
virtual bool supports_builtin_mode() const override;
@@ -487,6 +489,7 @@ public:
virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override;
virtual void profiling_stop() override;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 85ad08ea4f..8b4c245bf6 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -655,43 +655,43 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
}
- } else {
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ }
- if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
- }
- } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
- parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
- }
- if (member.variable->initializer->get_datatype().is_variant()) {
- // TODO: Warn unsafe assign.
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = specified_type;
+
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
+ } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
}
- } else if (member.variable->infer_datatype) {
- if (member.variable->initializer == nullptr) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
- } else if (!datatype.is_set() || datatype.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.is_variant()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.builtin_type == Variant::NIL) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
+ mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
+ }
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
datatype.is_constant = false;
@@ -860,6 +860,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
break;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ // No-op, but needed to silence warnings.
+ break;
case GDScriptParser::ClassNode::Member::UNDEFINED:
ERR_PRINT("Trying to resolve undefined member.");
break;
@@ -898,7 +901,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -947,7 +950,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -1160,8 +1163,16 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
}
} else {
- GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
- p_function->set_datatype(return_type);
+ if (p_function->return_type != nullptr) {
+ p_function->set_datatype(resolve_datatype(p_function->return_type));
+ } else {
+ // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here.
+ // It's not "undetected" to not mix up with unknown functions.
+ GDScriptParser::DataType return_type;
+ return_type.type_source = GDScriptParser::DataType::INFERRED;
+ return_type.kind = GDScriptParser::DataType::VARIANT;
+ p_function->set_datatype(return_type);
+ }
#ifdef TOOLS_ENABLED
// Check if the function signature matches the parent. If not it's an error since it breaks polymorphism.
@@ -1231,7 +1242,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
GDScriptParser::DataType return_type = p_function->body->get_datatype();
- if (p_function->get_datatype().has_no_type() && return_type.is_set()) {
+ if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) {
// Use the suite inferred type if return isn't explicitly set.
return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
@@ -1279,7 +1290,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : stmt->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -1514,10 +1525,22 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
+ GDScriptParser::DataType explicit_type;
+ if (p_constant->datatype_specifier != nullptr) {
+ explicit_type = resolve_datatype(p_constant->datatype_specifier);
+ explicit_type.is_meta_type = false;
+ }
+
if (p_constant->initializer != nullptr) {
reduce_expression(p_constant->initializer);
if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer);
+ const_fold_array(array);
+
+ // Can only infer typed array if it has elements.
+ if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) {
+ update_array_literal_element_type(explicit_type, array);
+ }
} else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
}
@@ -1536,8 +1559,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
}
if (p_constant->datatype_specifier != nullptr) {
- GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
- explicit_type.is_meta_type = false;
if (!is_type_compatible(explicit_type, type)) {
push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
#ifdef DEBUG_ENABLED
@@ -1639,8 +1660,8 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
is_shadowing(p_match_pattern->bind, "pattern bind");
- if (p_match_pattern->bind->usages == 0) {
- parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name);
+ if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
+ parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
#endif
break;
@@ -2057,7 +2078,8 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
p_await->set_datatype(awaiting_type);
#ifdef DEBUG_ENABLED
- if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) {
+ awaiting_type = p_await->to_await->get_datatype();
+ if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) {
parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT);
}
#endif
@@ -2174,7 +2196,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
- Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
+ HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
for (int i = 0; i < p_call->arguments.size(); i++) {
reduce_expression(p_call->arguments[i]);
if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
@@ -2259,6 +2281,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
p_call->is_constant = true;
@@ -2361,6 +2384,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2403,6 +2427,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2881,7 +2906,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
bool valid = false;
- int int_constant = ClassDB::get_integer_constant(native, name, &valid);
+ int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid);
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 519e1975c4..3966b81b6e 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -33,7 +33,7 @@
#include "core/object/object.h"
#include "core/object/ref_counted.h"
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 3d5a39bf38..6a1effd680 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -1336,6 +1336,18 @@ void GDScriptByteCodeGenerator::write_endif() {
if_jmp_addrs.pop_back();
}
+void GDScriptByteCodeGenerator::write_jump_if_shared(const Address &p_value) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_SHARED, 1);
+ append(p_value);
+ if_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+}
+
void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 0503f66161..f4b402fc96 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -53,19 +53,19 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
bool debug_stack = false;
Vector<int> opcodes;
- List<Map<StringName, int>> stack_id_stack;
- Map<StringName, int> stack_identifiers;
+ List<RBMap<StringName, int>> stack_id_stack;
+ RBMap<StringName, int> stack_identifiers;
List<int> stack_identifiers_counts;
- Map<StringName, int> local_constants;
+ RBMap<StringName, int> local_constants;
Vector<StackSlot> locals;
Vector<StackSlot> temporaries;
List<int> used_temporaries;
- Map<Variant::Type, List<int>> temporaries_pool;
+ RBMap<Variant::Type, List<int>> temporaries_pool;
List<GDScriptFunction::StackDebug> stack_debug;
- List<Map<StringName, int>> block_identifier_stack;
- Map<StringName, int> block_identifiers;
+ List<RBMap<StringName, int>> block_identifier_stack;
+ RBMap<StringName, int> block_identifiers;
int max_locals = 0;
int current_line = 0;
@@ -77,23 +77,23 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
#endif
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
- Map<StringName, int> name_map;
+ RBMap<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
#endif
- Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
- Map<Variant::ValidatedSetter, int> setters_map;
- Map<Variant::ValidatedGetter, int> getters_map;
- Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
- Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
- Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
- Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
- Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
- Map<Variant::ValidatedConstructor, int> constructors_map;
- Map<Variant::ValidatedUtilityFunction, int> utilities_map;
- Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
- Map<MethodBind *, int> method_bind_map;
- Map<GDScriptFunction *, int> lambdas_map;
+ RBMap<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+ RBMap<Variant::ValidatedSetter, int> setters_map;
+ RBMap<Variant::ValidatedGetter, int> getters_map;
+ RBMap<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+ RBMap<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+ RBMap<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+ RBMap<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+ RBMap<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+ RBMap<Variant::ValidatedConstructor, int> constructors_map;
+ RBMap<Variant::ValidatedUtilityFunction, int> utilities_map;
+ RBMap<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
+ RBMap<MethodBind *, int> method_bind_map;
+ RBMap<GDScriptFunction *, int> lambdas_map;
// Lists since these can be nested.
List<int> if_jmp_addrs;
@@ -135,7 +135,7 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
stack_identifiers_counts.push_back(locals.size());
stack_id_stack.push_back(stack_identifiers);
if (debug_stack) {
- Map<StringName, int> block_ids(block_identifiers);
+ RBMap<StringName, int> block_ids(block_identifiers);
block_identifier_stack.push_back(block_ids);
block_identifiers.clear();
}
@@ -479,6 +479,8 @@ public:
virtual void write_if(const Address &p_condition) override;
virtual void write_else() override;
virtual void write_endif() override;
+ virtual void write_jump_if_shared(const Address &p_value) override;
+ virtual void write_end_jump_if_shared() override;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override;
virtual void write_for() override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 8c198345c2..48d5fbc569 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -157,7 +157,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
source_file.write[len] = 0;
String source;
- if (source.parse_utf8((const char *)source_file.ptr())) {
+ if (source.parse_utf8((const char *)source_file.ptr()) != OK) {
ERR_FAIL_V_MSG("", "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode.");
}
return source;
@@ -223,13 +223,13 @@ Error GDScriptCache::finish_compiling(const String &p_owner) {
singleton->full_gdscript_cache[p_owner] = script.ptr();
singleton->shallow_gdscript_cache.erase(p_owner);
- Set<String> depends = singleton->dependencies[p_owner];
+ HashSet<String> depends = singleton->dependencies[p_owner];
Error err = OK;
- for (const Set<String>::Element *E = depends.front(); E != nullptr; E = E->next()) {
+ for (const String &E : depends) {
Error this_err = OK;
// No need to save the script. We assume it's already referenced in the owner.
- get_full_script(E->get(), this_err);
+ get_full_script(E, this_err);
if (this_err != OK) {
err = this_err;
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 3ce976ee14..b971bdd984 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -34,7 +34,7 @@
#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/templates/hash_map.h"
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript.h"
class GDScriptAnalyzer;
@@ -74,7 +74,7 @@ class GDScriptCache {
HashMap<String, GDScriptParserRef *> parser_map;
HashMap<String, GDScript *> shallow_gdscript_cache;
HashMap<String, GDScript *> full_gdscript_cache;
- HashMap<String, Set<String>> dependencies;
+ HashMap<String, HashSet<String>> dependencies;
friend class GDScript;
friend class GDScriptParserRef;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 326b66a295..81fa265aca 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -140,6 +140,8 @@ public:
virtual void write_if(const Address &p_condition) = 0;
virtual void write_else() = 0;
virtual void write_endif() = 0;
+ virtual void write_jump_if_shared(const Address &p_value) = 0;
+ virtual void write_end_jump_if_shared() = 0;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0;
virtual void write_for() = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index c194fbf9b8..e36252ada5 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.locals.has(p_name)) {
+ if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) {
return false; //shadowed
}
@@ -108,11 +108,15 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
- // Locate class by constructing the path to it and following that path
+ // Locate class by constructing the path to it and following that path.
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path);
- const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name);
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.builtin_type = p_datatype.builtin_type;
+
+ String class_name = class_type->fqcn.split("::")[0];
+ const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_name == main_script->path);
+ const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_name == main_script->name);
if (is_inner_by_path || is_inner_by_name) {
// Local class.
List<StringName> names;
@@ -131,16 +135,41 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
script = script->subclasses[names.back()->get()];
names.pop_back();
}
- result.kind = GDScriptDataType::GDSCRIPT;
result.script_type = script.ptr();
result.native_type = script->get_instance_base_type();
- result.builtin_type = p_datatype.builtin_type;
} else {
- result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ // Inner class.
+ PackedStringArray classes = class_type->fqcn.split("::");
+ if (!classes.is_empty()) {
+ for (GDScript *script : parsed_classes) {
+ // Checking of inheritance structure of inner class to find a correct script link.
+ if (script->name == classes[classes.size() - 1]) {
+ PackedStringArray classes2 = script->fully_qualified_name.split("::");
+ bool valid = true;
+ if (classes.size() != classes2.size()) {
+ valid = false;
+ } else {
+ for (int i = 0; i < classes.size(); i++) {
+ if (classes[i] != classes2[i]) {
+ valid = false;
+ break;
+ }
+ }
+ }
+ if (!valid) {
+ continue;
+ }
+ result.script_type_ref = Ref<GDScript>(script);
+ break;
+ }
+ }
+ }
+ if (result.script_type_ref.is_null()) {
+ result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ }
+
result.script_type = result.script_type_ref.ptr();
result.native_type = p_datatype.native_type;
- result.builtin_type = p_datatype.builtin_type;
}
}
} break;
@@ -283,7 +312,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Class C++ integer constant.
if (nc) {
bool success = false;
- int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
if (success) {
return codegen.add_constant(constant);
}
@@ -638,20 +667,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::GET_NODE: {
const GDScriptParser::GetNodeNode *get_node = static_cast<const GDScriptParser::GetNodeNode *>(p_expression);
- String node_name;
- if (get_node->string != nullptr) {
- node_name += String(get_node->string->value);
- } else {
- for (int i = 0; i < get_node->chain.size(); i++) {
- if (i > 0) {
- node_name += "/";
- }
- node_name += get_node->chain[i]->name;
- }
- }
-
Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(codegen.add_constant(NodePath(node_name)));
+ args.push_back(codegen.add_constant(NodePath(get_node->full_path)));
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
@@ -703,10 +720,10 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else if (subscript->is_attribute) {
if (subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
GDScriptParser::IdentifierNode *identifier = subscript->attribute;
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(identifier->name);
+ HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(identifier->name);
#ifdef DEBUG_ENABLED
- if (MI && MI->get().getter == codegen.function_name) {
+ if (MI && MI->value.getter == codegen.function_name) {
String n = identifier->name;
_set_error("Must use '" + n + "' instead of 'self." + n + "' in getter.", identifier);
r_error = ERR_COMPILATION_FAILED;
@@ -714,11 +731,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
#endif
- if (MI && MI->get().getter == "") {
+ if (MI && MI->value.getter == "") {
// Remove result temp as we don't need it.
gen->pop_temporary();
// Faster than indexing self (as if no self. had been used).
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->get().index, _gdtype_from_datatype(subscript->get_datatype()));
+ return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, MI->value.index, _gdtype_from_datatype(subscript->get_datatype()));
}
}
@@ -894,8 +911,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
const GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(assignment->assignee);
#ifdef DEBUG_ENABLED
if (subscript->is_attribute && subscript->base->type == GDScriptParser::Node::SELF && codegen.script) {
- const Map<StringName, GDScript::MemberInfo>::Element *MI = codegen.script->member_indices.find(subscript->attribute->name);
- if (MI && MI->get().setter == codegen.function_name) {
+ HashMap<StringName, GDScript::MemberInfo>::Iterator MI = codegen.script->member_indices.find(subscript->attribute->name);
+ if (MI && MI->value.setter == codegen.function_name) {
String n = subscript->attribute->name;
_set_error("Must use '" + n + "' instead of 'self." + n + "' in setter.", subscript);
r_error = ERR_COMPILATION_FAILED;
@@ -1039,13 +1056,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Set back the values into their bases.
for (const ChainInfo &info : set_chain) {
- if (!info.is_named) {
- gen->write_set(info.base, info.key, assigned);
- if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type || !is_shared) {
+ if (!known_type) {
+ // Jump shared values since they are already updated in-place.
+ gen->write_jump_if_shared(assigned);
+ }
+ if (!info.is_named) {
+ gen->write_set(info.base, info.key, assigned);
+ } else {
+ gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
}
- } else {
- gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (!info.is_named && info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
}
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -1053,19 +1082,35 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
assigned = info.base;
}
- // If this is a class member property, also assign to it.
- // This allow things like: position.x += 2.0
- if (assign_class_member_property != StringName()) {
- gen->write_set_member(assigned, assign_class_member_property);
- }
- // Same as above but for members
- if (is_member_property) {
- if (member_property_has_setter && !member_property_is_in_setter) {
- Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(assigned);
- gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
- } else {
- gen->write_assign(target_member_property, assigned);
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type || !is_shared) {
+ // If this is a class member property, also assign to it.
+ // This allow things like: position.x += 2.0
+ if (assign_class_member_property != StringName()) {
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ gen->write_set_member(assigned, assign_class_member_property);
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
+ } else if (is_member_property) {
+ // Same as above but for script members.
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ if (member_property_has_setter && !member_property_is_in_setter) {
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(assigned);
+ gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
+ } else {
+ gen->write_assign(target_member_property, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
}
}
@@ -1372,25 +1417,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
- if (p_is_nested) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_and_right_operand(result_addr);
- codegen.generator->write_end_and(p_previous_test);
- } else if (!p_is_first) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_or_right_operand(result_addr);
- codegen.generator->write_end_or(p_previous_test);
- } else {
- // Just assign this value to the accumulator temporary.
- codegen.generator->write_assign(p_previous_test, result_addr);
- }
- codegen.generator->pop_temporary(); // Remove temp result addr.
-
// Create temporaries outside the loop so they can be reused.
GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
- GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->array.size(); i++) {
@@ -1400,7 +1429,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Add index to constant map.
GDScriptCodeGenerator::Address index_addr = codegen.add_constant(i);
@@ -1414,19 +1443,34 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
- test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, result_addr, false, true);
if (r_error != OK) {
return GDScriptCodeGenerator::Address();
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- return test_addr;
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_DICTIONARY: {
if (p_is_nested) {
@@ -1471,27 +1515,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
- if (p_is_nested) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_and_right_operand(result_addr);
- codegen.generator->write_end_and(p_previous_test);
- } else if (!p_is_first) {
- // Use the previous value as target, since we only need one temporary variable.
- codegen.generator->write_or_right_operand(result_addr);
- codegen.generator->write_end_or(p_previous_test);
- } else {
- // Just assign this value to the accumulator temporary.
- codegen.generator->write_assign(p_previous_test, result_addr);
- }
- codegen.generator->pop_temporary(); // Remove temp result addr.
-
// Create temporaries outside the loop so they can be reused.
- temp_type.builtin_type = Variant::BOOL;
- GDScriptCodeGenerator::Address test_result = codegen.add_temporary(temp_type);
GDScriptCodeGenerator::Address element_addr = codegen.add_temporary();
GDScriptCodeGenerator::Address element_type_addr = codegen.add_temporary();
- GDScriptCodeGenerator::Address test_addr = p_previous_test;
// Evaluate element by element.
for (int i = 0; i < p_pattern->dictionary.size(); i++) {
@@ -1502,7 +1528,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
}
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_addr);
+ codegen.generator->write_and_left_operand(result_addr);
// Get the pattern key.
GDScriptCodeGenerator::Address pattern_key_addr = _parse_expression(codegen, r_error, element.key);
@@ -1513,11 +1539,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check if pattern key exists in user's dictionary. This will be AND-ed with next result.
func_args.clear();
func_args.push_back(pattern_key_addr);
- codegen.generator->write_call(test_result, p_value_addr, "has", func_args);
+ codegen.generator->write_call(result_addr, p_value_addr, "has", func_args);
if (element.value_pattern != nullptr) {
// Use AND here too, as we don't want to be checking elements if previous test failed (which means this might be an invalid get).
- codegen.generator->write_and_left_operand(test_result);
+ codegen.generator->write_and_left_operand(result_addr);
// Get actual value from user dictionary.
codegen.generator->write_get(element_addr, pattern_key_addr, p_value_addr);
@@ -1528,16 +1554,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
- test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
+ result_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, result_addr, false, true);
if (r_error != OK) {
return GDScriptCodeGenerator::Address();
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
}
- codegen.generator->write_and_right_operand(test_addr);
- codegen.generator->write_end_and(test_addr);
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(result_addr);
// Remove pattern key temporary.
if (pattern_key_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1548,9 +1574,23 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Remove element temporaries.
codegen.generator->pop_temporary();
codegen.generator->pop_temporary();
- codegen.generator->pop_temporary();
- return test_addr;
+ // If this isn't the first, we need to OR with the previous pattern. If it's nested, we use AND instead.
+ if (p_is_nested) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_and_right_operand(result_addr);
+ codegen.generator->write_end_and(p_previous_test);
+ } else if (!p_is_first) {
+ // Use the previous value as target, since we only need one temporary variable.
+ codegen.generator->write_or_right_operand(result_addr);
+ codegen.generator->write_end_or(p_previous_test);
+ } else {
+ // Just assign this value to the accumulator temporary.
+ codegen.generator->write_assign(p_previous_test, result_addr);
+ }
+ codegen.generator->pop_temporary(); // Remove temp result addr.
+
+ return p_previous_test;
} break;
case GDScriptParser::PatternNode::PT_REST:
// Do nothing.
@@ -1978,18 +2018,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
// Parse initializer if applies.
bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;
- bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
- bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready");
+ bool is_initializer = p_func && !p_for_lambda && p_func->identifier->name == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_implicit_ready = !p_func && p_for_ready;
- if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) {
+ if (!p_for_lambda && (is_implicit_initializer || is_implicit_ready)) {
// Initialize class fields.
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
continue;
}
const GDScriptParser::VariableNode *field = p_class->members[i].variable;
- if (field->onready != is_for_ready) {
- // Only initialize in _ready.
+ if (field->onready != is_implicit_ready) {
+ // Only initialize in @implicit_ready.
continue;
}
@@ -2111,6 +2151,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
p_script->initializer = gd_function;
} else if (is_implicit_initializer) {
p_script->implicit_initializer = gd_function;
+ } else if (is_implicit_ready) {
+ p_script->implicit_ready = gd_function;
}
if (p_func) {
@@ -2128,7 +2170,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
#endif
}
- if (!p_for_lambda) {
+ if (!is_implicit_initializer && !is_implicit_ready && !p_for_lambda) {
p_script->member_functions[func_name] = gd_function;
}
@@ -2196,11 +2238,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
memdelete(E.value);
}
+ if (p_script->implicit_initializer) {
+ memdelete(p_script->implicit_initializer);
+ }
+ if (p_script->implicit_ready) {
+ memdelete(p_script->implicit_ready);
+ }
p_script->member_functions.clear();
p_script->member_indices.clear();
p_script->member_info.clear();
p_script->_signals.clear();
p_script->initializer = nullptr;
+ p_script->implicit_initializer = nullptr;
+ p_script->implicit_ready = nullptr;
p_script->tool = parser->is_tool();
p_script->name = p_class->identifier ? p_class->identifier->name : "";
@@ -2402,6 +2452,25 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
#endif
} break;
+
+ case GDScriptParser::ClassNode::Member::GROUP: {
+ const GDScriptParser::AnnotationNode *annotation = member.annotation;
+ StringName name = annotation->export_info.name;
+
+ // This is not a normal member, but we need this to keep indices in order.
+ GDScript::MemberInfo minfo;
+ minfo.index = p_script->member_indices.size();
+
+ PropertyInfo prop_info;
+ prop_info.name = name;
+ prop_info.usage = annotation->export_info.usage;
+ prop_info.hint_string = annotation->export_info.hint_string;
+
+ p_script->member_info[name] = prop_info;
+ p_script->member_indices[name] = minfo;
+ p_script->members.insert(name);
+ } break;
+
default:
break; // Nothing to do here.
}
@@ -2444,15 +2513,10 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
//parse methods
- bool has_ready = false;
-
for (int i = 0; i < p_class->members.size(); i++) {
const GDScriptParser::ClassNode::Member &member = p_class->members[i];
if (member.type == member.FUNCTION) {
const GDScriptParser::FunctionNode *function = member.function;
- if (!has_ready && function->identifier->name == "_ready") {
- has_ready = true;
- }
Error err = OK;
_parse_function(err, p_script, p_class, function);
if (err) {
@@ -2486,8 +2550,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
}
- if (!has_ready && p_class->onready_used) {
- //create a _ready constructor
+ if (p_class->onready_used) {
+ // Create an implicit_ready constructor.
Error err = OK;
_parse_function(err, p_script, p_class, nullptr, true);
if (err) {
@@ -2500,8 +2564,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
//validate instances if keeping state
if (p_keep_state) {
- for (Set<Object *>::Element *E = p_script->instances.front(); E;) {
- Set<Object *>::Element *N = E->next();
+ for (RBSet<Object *>::Element *E = p_script->instances.front(); E;) {
+ RBSet<Object *>::Element *N = E->next();
ScriptInstance *si = E->get()->get_script_instance();
if (si->is_placeholder()) {
@@ -2563,7 +2627,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
}
void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
- Map<StringName, Ref<GDScript>> old_subclasses;
+ HashMap<StringName, Ref<GDScript>> old_subclasses;
if (p_keep_state) {
old_subclasses = p_script->subclasses;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 8d71437344..4841884e2d 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
-#include "core/templates/set.h"
+#include "core/templates/hash_set.h"
#include "gdscript.h"
#include "gdscript_codegen.h"
#include "gdscript_function.h"
@@ -39,8 +39,8 @@
class GDScriptCompiler {
const GDScriptParser *parser = nullptr;
- Set<GDScript *> parsed_classes;
- Set<GDScript *> parsing_classes;
+ HashSet<GDScript *> parsed_classes;
+ HashSet<GDScript *> parsing_classes;
GDScript *main_script = nullptr;
struct CodeGen {
@@ -49,9 +49,9 @@ class GDScriptCompiler {
const GDScriptParser::FunctionNode *function_node = nullptr;
StringName function_name;
GDScriptCodeGenerator *generator = nullptr;
- Map<StringName, GDScriptCodeGenerator::Address> parameters;
- Map<StringName, GDScriptCodeGenerator::Address> locals;
- List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack;
+ HashMap<StringName, GDScriptCodeGenerator::Address> parameters;
+ HashMap<StringName, GDScriptCodeGenerator::Address> locals;
+ List<HashMap<StringName, GDScriptCodeGenerator::Address>> locals_stack;
GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
uint32_t addr = generator->add_local(p_name, p_type);
@@ -101,7 +101,7 @@ class GDScriptCompiler {
}
void start_block() {
- Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
+ HashMap<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
locals_stack.push_back(old_locals);
generator->start_block();
}
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index dc114f2eff..726f0efe2b 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -838,6 +838,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 1;
} break;
+ case OPCODE_JUMP_IF_SHARED: {
+ text += "jump-if-shared ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
case OPCODE_RETURN: {
text += "return ";
text += DADDR(1);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 226b4c24b1..90dcfa307e 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -98,7 +98,7 @@ Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(
return templates;
}
-static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, Map<int, String> &r_funcs) {
+static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, HashMap<int, String> &r_funcs) {
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {
const GDScriptParser::FunctionNode *function = p_class->members[i].function;
@@ -110,7 +110,7 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl
}
}
-bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const {
GDScriptParser parser;
GDScriptAnalyzer analyzer(&parser);
@@ -148,7 +148,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
return false;
} else {
const GDScriptParser::ClassNode *cl = parser.get_tree();
- Map<int, String> funcs;
+ HashMap<int, String> funcs;
get_function_names_recursively(cl, "", funcs);
@@ -159,7 +159,7 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
#ifdef DEBUG_ENABLED
if (r_safe_lines) {
- const Set<int> &unsafe_lines = parser.get_unsafe_lines();
+ const HashSet<int> &unsafe_lines = parser.get_unsafe_lines();
for (int i = 1; i <= parser.get_last_line_number(); i++) {
if (!unsafe_lines.has(i)) {
r_safe_lines->insert(i);
@@ -321,7 +321,7 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
Ref<GDScript> script = instance->get_script();
ERR_FAIL_COND(script.is_null());
- const Map<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
+ const HashMap<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {
p_members->push_back(E.key);
@@ -343,7 +343,7 @@ ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {
}
void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- const Map<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();
+ const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();
const Variant *globals = GDScriptLanguage::get_singleton()->get_global_array();
List<Pair<String, Variant>> cinfo;
@@ -445,6 +445,16 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const
p_constants->push_back(nan);
}
+void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
+ GDScriptParser parser;
+ List<MethodInfo> annotations;
+ parser.get_annotation_list(&annotations);
+
+ for (const MethodInfo &E : annotations) {
+ p_annotations->push_back(E);
+ }
+}
+
String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {
#ifdef TOOLS_ENABLED
bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");
@@ -569,7 +579,7 @@ static int _get_enum_constant_location(StringName p_class, StringName p_enum_con
// END LOCATION METHODS
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
- if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
String enum_name = p_info.class_name;
if (!enum_name.contains(".")) {
return enum_name;
@@ -722,7 +732,7 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
return arghint;
}
-static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptLanguage::CodeCompletionOption> &r_list) {
+static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
for (int i = 0; i < p_dir->get_file_count(); i++) {
@@ -736,9 +746,9 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String
}
}
-static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (p_annotation->name == SNAME("@export_range")) {
- if (p_argument == 3 || p_argument == 4) {
+ if (p_argument == 3 || p_argument == 4 || p_argument == 5) {
// Slider hint.
ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider1.insert_text = slider1.display.quote(p_quote_style);
@@ -746,6 +756,9 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
ScriptLanguage::CodeCompletionOption slider2("or_lesser", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
slider2.insert_text = slider2.display.quote(p_quote_style);
r_result.insert(slider2.display, slider2);
+ ScriptLanguage::CodeCompletionOption slider3("no_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ slider3.insert_text = slider3.display.quote(p_quote_style);
+ r_result.insert(slider3.display, slider3);
}
} else if (p_annotation->name == SNAME("@export_exp_easing")) {
if (p_argument == 0 || p_argument == 1) {
@@ -777,7 +790,7 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
}
}
-static void _find_built_in_variants(Map<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {
+static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) {
ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);
@@ -789,7 +802,7 @@ static void _find_built_in_variants(Map<String, ScriptLanguage::CodeCompletionOp
}
}
-static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
// Built-in Variant Types
_find_built_in_variants(r_result, true);
@@ -863,7 +876,7 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
}
}
-static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
for (int i = 0; i < p_suite->locals.size(); i++) {
ScriptLanguage::CodeCompletionOption option;
if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {
@@ -879,9 +892,9 @@ static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite,
}
}
-static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);
-static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
+static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_static, bool p_parent_only, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
if (!p_parent_only) {
@@ -947,6 +960,8 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
}
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
break;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case GDScriptParser::ClassNode::Member::UNDEFINED:
break;
}
@@ -966,7 +981,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
_find_identifiers_in_base(base_type, p_only_functions, r_result, p_recursion_depth + 1);
}
-static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
+static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);
GDScriptParser::DataType base_type = p_base.type;
@@ -998,7 +1013,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
r_result.insert(option.display, option);
}
}
- Map<StringName, Variant> constants;
+ HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
for (const KeyValue<StringName, Variant> &E : constants) {
int location = p_recursion_depth + _get_constant_location(scr->get_class_name(), E.key);
@@ -1149,7 +1164,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
}
-static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
+static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {
if (!p_only_functions && p_context.current_suite) {
// This includes function parameters, since they are also locals.
_find_identifiers_in_suite(p_context.current_suite, r_result);
@@ -1289,7 +1304,7 @@ static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_pr
return ci;
}
- if (p_property.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
ci.enumeration = p_property.class_name;
}
@@ -1840,7 +1855,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
while (suite) {
for (int i = 0; i < suite->statements.size(); i++) {
- if (suite->statements[i]->start_line > p_context.current_line) {
+ if (suite->statements[i]->end_line >= p_context.current_line) {
break;
}
@@ -1888,7 +1903,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
suite = suite->parent_block;
}
- if (last_assigned_expression && last_assign_line != p_context.current_line) {
+ if (last_assigned_expression && last_assign_line < p_context.current_line) {
GDScriptParser::CompletionContext c = p_context;
c.current_line = last_assign_line;
r_type.assigned_expression = last_assigned_expression;
@@ -1989,8 +2004,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return false;
}
- // Check autoloads.
- if (ProjectSettings::get_singleton()->has_autoload(p_identifier)) {
+ // Check global variables (including autoloads).
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
return true;
}
@@ -2025,7 +2040,10 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
return true;
case GDScriptParser::ClassNode::Member::VARIABLE:
if (!is_static) {
- if (member.variable->initializer) {
+ if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
+ r_type.type = member.variable->get_datatype();
+ return true;
+ } else if (member.variable->initializer) {
const GDScriptParser::ExpressionNode *init = member.variable->initializer;
if (init->is_constant) {
r_type.value = init->reduced_value;
@@ -2047,9 +2065,6 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type = init->get_datatype();
return true;
}
- } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
- r_type.type = member.variable->get_datatype();
- return true;
}
}
// TODO: Check assignments in constructor.
@@ -2079,6 +2094,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
r_type.type.kind = GDScriptParser::DataType::CLASS;
r_type.type.class_type = member.m_class;
return true;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ return false; // No-op, but silences warnings.
case GDScriptParser::ClassNode::Member::UNDEFINED:
return false; // Unreachable.
}
@@ -2089,7 +2106,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
case GDScriptParser::DataType::SCRIPT: {
Ref<Script> scr = base_type.script_type;
if (scr.is_valid()) {
- Map<StringName, Variant> constants;
+ HashMap<StringName, Variant> constants;
scr->get_constants(&constants);
if (constants.has(p_identifier)) {
r_type = _type_from_variant(constants[p_identifier]);
@@ -2318,7 +2335,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
return false;
}
-static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, Map<String, ScriptLanguage::CodeCompletionOption> &r_result) {
+static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {
if (!p_enum_hint.contains(".")) {
// Global constant or in the current class.
StringName current_enum = p_enum_hint;
@@ -2355,7 +2372,7 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
}
}
-static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, int p_argidx, bool p_static, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {
Variant base = p_base.value;
GDScriptParser::DataType base_type = p_base.type;
@@ -2404,7 +2421,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (p_argidx < method_args) {
PropertyInfo arg_info = info.arguments[p_argidx];
- if (arg_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
_find_enumeration_candidates(p_context, arg_info.class_name, r_result);
}
}
@@ -2474,7 +2491,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
}
-static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
+static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
if (p_call->type == GDScriptParser::Node::PRELOAD) {
if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
@@ -2589,7 +2606,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
analyzer.analyze();
r_forced = false;
- Map<String, ScriptLanguage::CodeCompletionOption> options;
+ HashMap<String, ScriptLanguage::CodeCompletionOption> options;
GDScriptParser::CompletionContext completion_context = parser.get_completion_context();
completion_context.base = p_owner;
@@ -3155,7 +3172,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
- // Before parsing, try the usual stuff
+ // Before parsing, try the usual stuff.
if (ClassDB::class_exists(p_symbol)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;
r_result.class_name = p_symbol;
@@ -3171,7 +3188,9 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
- if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+ // Need special checks for assert and preload as they are technically
+ // keywords, so are not registered in GDScriptUtilityFunctions.
+ if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;
r_result.class_name = "@GDScript";
r_result.class_member = p_symbol;
@@ -3227,6 +3246,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
is_function = true;
[[fallthrough]];
}
+ case GDScriptParser::COMPLETION_ASSIGN:
case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER: {
GDScriptParser::DataType base_type;
@@ -3279,7 +3299,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
// Global.
- Map<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
+ HashMap<StringName, int> classes = GDScriptLanguage::get_singleton()->get_global_map();
if (classes.has(p_symbol)) {
Variant value = GDScriptLanguage::get_singleton()->get_global_array()[classes[p_symbol]];
if (value.get_type() == Variant::OBJECT) {
@@ -3370,6 +3390,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK;
}
} break;
+ case GDScriptParser::COMPLETION_ANNOTATION: {
+ const String annotation_symbol = "@" + p_symbol;
+ if (parser.annotation_exists(annotation_symbol)) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = annotation_symbol;
+ return OK;
+ }
+ } break;
default: {
}
}
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 3d708955ed..cd3b7d69c5 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -93,7 +93,7 @@ struct _GDFKCS {
void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {
int oc = 0;
- Map<StringName, _GDFKC> sdmap;
+ HashMap<StringName, _GDFKC> sdmap;
for (const StackDebug &sd : stack_debug) {
if (sd.line >= p_line) {
break;
@@ -270,6 +270,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->exit_function();
}
+
+ _clear_stack();
#endif
}
@@ -279,7 +281,8 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
void GDScriptFunctionState::_clear_stack() {
if (state.stack_size) {
Variant *stack = (Variant *)state.stack.ptr();
- for (int i = 0; i < state.stack_size; i++) {
+ // The first 3 are special addresses and not copied to the state, so we skip them here.
+ for (int i = 3; i < state.stack_size; i++) {
stack[i].~Variant();
}
state.stack_size = 0;
@@ -300,8 +303,6 @@ GDScriptFunctionState::GDScriptFunctionState() :
}
GDScriptFunctionState::~GDScriptFunctionState() {
- _clear_stack();
-
{
MutexLock lock(GDScriptLanguage::singleton->lock);
scripts_list.remove_from_list();
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index ba0d51c5cc..3f1265679b 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -304,6 +304,7 @@ public:
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
OPCODE_JUMP_TO_DEF_ARGUMENT,
+ OPCODE_JUMP_IF_SHARED,
OPCODE_RETURN,
OPCODE_RETURN_TYPED_BUILTIN,
OPCODE_RETURN_TYPED_ARRAY,
@@ -495,7 +496,7 @@ private:
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
- Map<int, Variant::Type> temporary_slots;
+ HashMap<int, Variant::Type> temporary_slots;
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index c43fa12c8c..a25bf9a306 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -91,7 +91,7 @@ GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptF
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -161,7 +161,7 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, G
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
@@ -169,5 +169,5 @@ GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptF
function = p_function;
captures = p_captures;
- h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+ h = (uint32_t)hash_murmur3_one_64((uint64_t)this);
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index aa1bfb312c..01a672c330 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -105,35 +105,44 @@ void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const
}
}
+bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
+ return valid_annotations.has(p_annotation_name);
+}
+
GDScriptParser::GDScriptParser() {
// Register valid annotations.
// TODO: Should this be static?
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
- register_annotation(MethodInfo("@icon", { Variant::STRING, "icon_path" }), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
+ register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_enum", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true);
- register_annotation(MethodInfo("@export_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, varray(), true);
+ register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
- register_annotation(MethodInfo("@export_global_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, 1, true);
+ register_annotation(MethodInfo("@export_global_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_global_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_GLOBAL_DIR, Variant::STRING>);
register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
- register_annotation(MethodInfo("@export_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 3);
- register_annotation(MethodInfo("@export_exp_easing", { Variant::STRING, "hint1" }, { Variant::STRING, "hint2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2);
+ register_annotation(MethodInfo("@export_range", PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"), PropertyInfo(Variant::FLOAT, "step"), PropertyInfo(Variant::STRING, "extra_hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, varray(1.0, ""), true);
+ register_annotation(MethodInfo("@export_exp_easing", PropertyInfo(Variant::STRING, "hints")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, varray(""), true);
register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
- register_annotation(MethodInfo("@export_node_path", { Variant::STRING, "type" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true);
- register_annotation(MethodInfo("@export_flags", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true);
+ register_annotation(MethodInfo("@export_node_path", PropertyInfo(Variant::STRING, "type")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, varray(""), true);
+ register_annotation(MethodInfo("@export_flags", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, varray(), true);
register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
- register_annotation(MethodInfo("@warning_ignore", { Variant::STRING, "warning" }), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
+ // Export grouping annotations.
+ register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
+ register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
+ register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray(""));
+ // Warning annotations.
+ register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray(), true);
// Networking.
- register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
+ register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, varray("", "", "", 0), true);
}
GDScriptParser::~GDScriptParser() {
@@ -153,6 +162,7 @@ void GDScriptParser::clear() {
for_completion = false;
errors.clear();
multiline_stack.clear();
+ nodes_in_progress.clear();
}
void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
@@ -203,7 +213,8 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
if (ignored_warnings.has(warn_name)) {
return;
}
- if (!GLOBAL_GET("debug/gdscript/warnings/" + warn_name)) {
+ int warn_level = (int)GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(p_code));
+ if (!warn_level) {
return;
}
@@ -215,6 +226,11 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.leftmost_column = p_source->leftmost_column;
warning.rightmost_column = p_source->rightmost_column;
+ if (warn_level == GDScriptWarning::WarnLevel::ERROR) {
+ push_error(warning.get_message(), p_source);
+ return;
+ }
+
List<GDScriptWarning>::Element *before = nullptr;
for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
if (E->get().start_line > warning.start_line) {
@@ -398,6 +414,9 @@ GDScriptTokenizer::Token GDScriptParser::advance() {
push_error(current.literal);
current = tokenizer.scan();
}
+ for (Node *n : nodes_in_progress) {
+ update_extents(n);
+ }
return previous;
}
@@ -513,9 +532,13 @@ void GDScriptParser::parse_program() {
head = alloc_node<ClassNode>();
current_class = head;
+ // If we happen to parse an annotation before extends or class_name keywords, track it.
+ // @tool is allowed, but others should fail.
+ AnnotationNode *premature_annotation = nullptr;
+
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @tool annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ // Check for @tool, script-level, or standalone annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
if (annotation->name == SNAME("@tool")) {
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
@@ -525,7 +548,14 @@ void GDScriptParser::parse_program() {
}
// @tool annotation has no specific target.
annotation->apply(this, nullptr);
+ } else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
+ premature_annotation = annotation;
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after a standalone annotation.)");
+ }
+ annotation->apply(this, head);
} else {
+ premature_annotation = annotation;
annotation_stack.push_back(annotation);
}
}
@@ -535,8 +565,8 @@ void GDScriptParser::parse_program() {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (!annotation_stack.is_empty()) {
- push_error(R"("class_name" should be used before annotations.)");
+ if (premature_annotation != nullptr) {
+ push_error(R"("class_name" should be used before annotations (except @tool).)");
}
advance();
if (head->identifier != nullptr) {
@@ -546,8 +576,8 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (!annotation_stack.is_empty()) {
- push_error(R"("extends" should be used before annotations.)");
+ if (premature_annotation != nullptr) {
+ push_error(R"("extends" should be used before annotations (except @tool).)");
}
advance();
if (head->extends_used) {
@@ -568,12 +598,12 @@ void GDScriptParser::parse_program() {
}
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
- // Check for @icon annotation.
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
+ // Check for a script-level, or standalone annotation.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- if (annotation->name == SNAME("@icon")) {
+ if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
- push_error(R"(Expected newline after "@icon" annotation.)");
+ push_error(R"(Expected newline after a standalone annotation.)");
}
annotation->apply(this, head);
} else {
@@ -583,6 +613,7 @@ void GDScriptParser::parse_program() {
}
parse_class_body(true);
+ complete_extents(head);
#ifdef TOOLS_ENABLED
for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
@@ -623,6 +654,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
current_class = previous_class;
+ complete_extents(n_class);
return n_class;
}
@@ -635,6 +667,7 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
}
parse_class_body(multiline);
+ complete_extents(n_class);
if (multiline) {
consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
@@ -801,9 +834,18 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
break;
case GDScriptTokenizer::Token::ANNOTATION: {
advance();
- AnnotationNode *annotation = parse_annotation(AnnotationInfo::CLASS_LEVEL);
+
+ // Check for class-level annotations.
+ AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
if (annotation != nullptr) {
- annotation_stack.push_back(annotation);
+ if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
+ push_error(R"(Expected newline after a standalone annotation.)");
+ }
+ annotation->apply(this, head);
+ } else {
+ annotation_stack.push_back(annotation);
+ }
}
break;
}
@@ -835,11 +877,13 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable() {
}
GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_property) {
+ VariableNode *variable = alloc_node<VariableNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected variable name after "var".)")) {
+ complete_extents(variable);
return nullptr;
}
- VariableNode *variable = alloc_node<VariableNode>();
variable->identifier = parse_identifier();
variable->export_info.name = variable->identifier->name;
@@ -847,10 +891,10 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
if (check(GDScriptTokenizer::Token::NEWLINE)) {
if (p_allow_property) {
advance();
-
return parse_property(variable, true);
} else {
push_error(R"(Expected type after ":")");
+ complete_extents(variable);
return nullptr;
}
} else if (check((GDScriptTokenizer::Token::EQUAL))) {
@@ -889,6 +933,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
}
}
+ complete_extents(variable);
end_statement("variable declaration");
return variable;
@@ -897,6 +942,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_variable, bool p_need_indent) {
if (p_need_indent) {
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block for property after ":".)")) {
+ complete_extents(p_variable);
return nullptr;
}
}
@@ -906,6 +952,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
make_completion_context(COMPLETION_PROPERTY_DECLARATION, property);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected "get" or "set" for property declaration.)")) {
+ complete_extents(p_variable);
return nullptr;
}
@@ -962,6 +1009,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
}
function = parse_identifier();
}
+ complete_extents(p_variable);
if (p_variable->property == VariableNode::PROP_SETGET) {
end_statement("property declaration");
@@ -976,37 +1024,37 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
switch (p_variable->property) {
case VariableNode::PROP_INLINE: {
- consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
- if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
- p_variable->setter_parameter = parse_identifier();
- }
- consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
- consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
-
+ FunctionNode *function = alloc_node<FunctionNode>();
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_setter";
-
- FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier;
- FunctionNode *previous_function = current_function;
- current_function = function;
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
ParameterNode *parameter = alloc_node<ParameterNode>();
- parameter->identifier = p_variable->setter_parameter;
-
- if (parameter->identifier != nullptr) {
+ if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
+ reset_extents(parameter, previous);
+ p_variable->setter_parameter = parse_identifier();
+ parameter->identifier = p_variable->setter_parameter;
function->parameters_indices[parameter->identifier->name] = 0;
function->parameters.push_back(parameter);
+ }
+ complete_extents(parameter);
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
+ consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+ if (p_variable->setter_parameter != nullptr) {
SuiteNode *body = alloc_node<SuiteNode>();
body->add_local(parameter, function);
-
function->body = parse_suite("setter declaration", body);
p_variable->setter = function;
}
-
current_function = previous_function;
+ complete_extents(function);
break;
}
case VariableNode::PROP_SETGET:
@@ -1024,12 +1072,13 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
switch (p_variable->property) {
case VariableNode::PROP_INLINE: {
+ FunctionNode *function = alloc_node<FunctionNode>();
+
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = "@" + p_variable->identifier->name + "_getter";
-
- FunctionNode *function = alloc_node<FunctionNode>();
function->identifier = identifier;
FunctionNode *previous_function = current_function;
@@ -1037,9 +1086,10 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
SuiteNode *body = alloc_node<SuiteNode>();
function->body = parse_suite("getter declaration", body);
-
p_variable->getter = function;
+
current_function = previous_function;
+ complete_extents(function);
break;
}
case VariableNode::PROP_SETGET:
@@ -1055,11 +1105,12 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
}
GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
+ ConstantNode *constant = alloc_node<ConstantNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
return nullptr;
}
- ConstantNode *constant = alloc_node<ConstantNode>();
constant->identifier = parse_identifier();
if (match(GDScriptTokenizer::Token::COLON)) {
@@ -1078,12 +1129,15 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
if (constant->initializer == nullptr) {
push_error(R"(Expected initializer expression for constant.)");
+ complete_extents(constant);
return nullptr;
}
} else {
+ complete_extents(constant);
return nullptr;
}
+ complete_extents(constant);
end_statement("constant declaration");
return constant;
@@ -1113,15 +1167,18 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
parameter->default_value = parse_expression(false);
}
+ complete_extents(parameter);
return parameter;
}
GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
+ SignalNode *signal = alloc_node<SignalNode>();
+
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected signal name after "signal".)")) {
+ complete_extents(signal);
return nullptr;
}
- SignalNode *signal = alloc_node<SignalNode>();
signal->identifier = parse_identifier();
if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
@@ -1153,6 +1210,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
}
+ complete_extents(signal);
end_statement("signal declaration");
return signal;
@@ -1264,6 +1322,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
}
#endif // TOOLS_ENABLED
+ complete_extents(enum_node);
end_statement("enum");
return enum_node;
@@ -1315,19 +1374,22 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
}
GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+ FunctionNode *function = alloc_node<FunctionNode>();
+
bool _static = false;
if (previous.type == GDScriptTokenizer::Token::STATIC) {
// TODO: Improve message if user uses "static" with "var" or "const"
if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
+ complete_extents(function);
return nullptr;
}
_static = true;
}
- FunctionNode *function = alloc_node<FunctionNode>();
make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
+ complete_extents(function);
return nullptr;
}
@@ -1349,6 +1411,7 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
function->body = parse_suite("function declaration", body);
current_function = previous_function;
+ complete_extents(function);
return function;
}
@@ -1396,6 +1459,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
}
pop_completion_call();
}
+ complete_extents(annotation);
match(GDScriptTokenizer::Token::NEWLINE); // Newline after annotation is optional.
@@ -1414,12 +1478,12 @@ void GDScriptParser::clear_unused_annotations() {
annotation_stack.clear();
}
-bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments, bool p_is_vararg) {
+bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments, bool p_is_vararg) {
ERR_FAIL_COND_V_MSG(valid_annotations.has(p_info.name), false, vformat(R"(Annotation "%s" already registered.)", p_info.name));
AnnotationInfo new_annotation;
new_annotation.info = p_info;
- new_annotation.info.default_arguments.resize(p_optional_arguments);
+ new_annotation.info.default_arguments = p_default_arguments;
if (p_is_vararg) {
new_annotation.info.flags |= METHOD_FLAG_VARARG;
}
@@ -1445,9 +1509,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
if (multiline) {
if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
current_suite = suite->parent_block;
+ complete_extents(suite);
return suite;
}
}
+ reset_extents(suite, current);
int error_count = 0;
@@ -1497,6 +1563,8 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
} while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
+ complete_extents(suite);
+
if (multiline) {
if (!lambda_ended) {
consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
@@ -1527,6 +1595,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::PASS:
advance();
result = alloc_node<PassNode>();
+ complete_extents(result);
end_statement(R"("pass")");
break;
case GDScriptTokenizer::Token::VAR:
@@ -1574,6 +1643,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
// If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
n_return->return_value = parse_expression(false);
}
+ complete_extents(n_return);
result = n_return;
current_suite->has_return = true;
@@ -1584,6 +1654,7 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case GDScriptTokenizer::Token::BREAKPOINT:
advance();
result = alloc_node<BreakpointNode>();
+ complete_extents(result);
end_statement(R"("breakpoint")");
break;
case GDScriptTokenizer::Token::ASSERT:
@@ -1609,10 +1680,12 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
lambda_ended = true;
has_ended_lambda = true;
} else {
+ advance();
push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
}
+ } else {
+ end_statement("expression");
}
- end_statement("expression");
lambda_ended = lambda_ended || has_ended_lambda;
result = expression;
@@ -1624,6 +1697,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case Node::AWAIT:
// Fine.
break;
+ case Node::LAMBDA:
+ // Standalone lambdas can't be used, so make this an error.
+ push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
+ break;
default:
push_warning(expression, GDScriptWarning::STANDALONE_EXPRESSION);
}
@@ -1671,6 +1748,7 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->condition = parse_expression(false);
if (assert->condition == nullptr) {
push_error("Expected expression to assert.");
+ complete_extents(assert);
return nullptr;
}
@@ -1679,12 +1757,14 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
assert->message = parse_expression(false);
if (assert->message == nullptr) {
push_error(R"(Expected error message for assert after ",".)");
+ complete_extents(assert);
return nullptr;
}
}
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after assert expression.)*");
+ complete_extents(assert);
end_statement(R"("assert")");
return assert;
@@ -1694,8 +1774,10 @@ GDScriptParser::BreakNode *GDScriptParser::parse_break() {
if (!can_break) {
push_error(R"(Cannot use "break" outside of a loop.)");
}
+ BreakNode *break_node = alloc_node<BreakNode>();
+ complete_extents(break_node);
end_statement(R"("break")");
- return alloc_node<BreakNode>();
+ return break_node;
}
GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
@@ -1703,9 +1785,10 @@ GDScriptParser::ContinueNode *GDScriptParser::parse_continue() {
push_error(R"(Cannot use "continue" outside of a loop or pattern matching block.)");
}
current_suite->has_continue = true;
- end_statement(R"("continue")");
ContinueNode *cont = alloc_node<ContinueNode>();
cont->is_for_match = is_continue_match;
+ complete_extents(cont);
+ end_statement(R"("continue")");
return cont;
}
@@ -1738,11 +1821,16 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
+ const SuiteNode::Local &local = current_suite->get_local(n_for->variable->name);
+ if (local.type != SuiteNode::Local::UNDEFINED) {
+ push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), n_for->variable->name), n_for->variable);
+ }
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite);
+ complete_extents(n_for);
// Reset break/continue state.
can_break = could_break;
@@ -1770,15 +1858,16 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
}
if (match(GDScriptTokenizer::Token::ELIF)) {
- IfNode *elif = parse_if("elif");
-
SuiteNode *else_block = alloc_node<SuiteNode>();
+ IfNode *elif = parse_if("elif");
else_block->statements.push_back(elif);
+ complete_extents(else_block);
n_if->false_block = else_block;
} else if (match(GDScriptTokenizer::Token::ELSE)) {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
n_if->false_block = parse_suite(R"("else" block)");
}
+ complete_extents(n_if);
if (n_if->false_block != nullptr && n_if->false_block->has_return && n_if->true_block->has_return) {
current_suite->has_return = true;
@@ -1802,13 +1891,13 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected a newline after "match" statement.)");
if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected an indented block after "match" statement.)")) {
+ complete_extents(match);
return match;
}
#ifdef DEBUG_ENABLED
bool all_have_return = true;
bool have_wildcard = false;
- bool wildcard_has_return = false;
bool have_wildcard_without_continue = false;
#endif
@@ -1820,15 +1909,12 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
}
#ifdef DEBUG_ENABLED
- if (have_wildcard_without_continue) {
+ if (have_wildcard_without_continue && !branch->patterns.is_empty()) {
push_warning(branch->patterns[0], GDScriptWarning::UNREACHABLE_PATTERN);
}
if (branch->has_wildcard) {
have_wildcard = true;
- if (branch->block->has_return) {
- wildcard_has_return = true;
- }
if (!branch->block->has_continue) {
have_wildcard_without_continue = true;
}
@@ -1839,11 +1925,12 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
#endif
match->branches.push_back(branch);
}
+ complete_extents(match);
consume(GDScriptTokenizer::Token::DEDENT, R"(Expected an indented block after "match" statement.)");
#ifdef DEBUG_ENABLED
- if (wildcard_has_return || (all_have_return && have_wildcard)) {
+ if (all_have_return && have_wildcard) {
current_suite->has_return = true;
}
#endif
@@ -1853,6 +1940,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
MatchBranchNode *branch = alloc_node<MatchBranchNode>();
+ reset_extents(branch, current);
bool has_bind = false;
@@ -1880,6 +1968,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
}
if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
+ complete_extents(branch);
return nullptr;
}
@@ -1900,6 +1989,7 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
}
branch->block = parse_suite("match pattern block", suite);
+ complete_extents(branch);
// Restore continue state.
can_continue = could_continue;
@@ -1910,12 +2000,14 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_root_pattern) {
PatternNode *pattern = alloc_node<PatternNode>();
+ reset_extents(pattern, current);
switch (current.type) {
case GDScriptTokenizer::Token::VAR: {
// Bind.
advance();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected bind name after "var".)")) {
+ complete_extents(pattern);
return nullptr;
}
pattern->pattern_type = PatternNode::PT_BIND;
@@ -1926,12 +2018,14 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
if (p_root_pattern != nullptr) {
if (p_root_pattern->has_bind(pattern->bind->name)) {
push_error(vformat(R"(Bind variable name "%s" was already used in this pattern.)", pattern->bind->name));
+ complete_extents(pattern);
return nullptr;
}
}
if (current_suite->has_local(pattern->bind->name)) {
push_error(vformat(R"(There's already a %s named "%s" in this scope.)", current_suite->get_local(pattern->bind->name).get_name(), pattern->bind->name));
+ complete_extents(pattern);
return nullptr;
}
@@ -1984,6 +2078,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
} else {
PatternNode *sub_pattern = alloc_node<PatternNode>();
+ complete_extents(sub_pattern);
sub_pattern->pattern_type = PatternNode::PT_REST;
pattern->dictionary.push_back({ nullptr, sub_pattern });
pattern->rest_used = true;
@@ -2032,6 +2127,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
break;
}
}
+ complete_extents(pattern);
return pattern;
}
@@ -2065,6 +2161,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
+ complete_extents(n_while);
// Reset break/continue state.
can_break = could_break;
@@ -2103,7 +2200,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL)) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -2137,6 +2234,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
}
IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ complete_extents(identifier);
identifier->name = previous.get_identifier();
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
@@ -2188,6 +2286,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_literal(ExpressionNode *p_
}
LiteralNode *literal = alloc_node<LiteralNode>();
+ complete_extents(literal);
literal->value = previous.literal;
return literal;
}
@@ -2197,6 +2296,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
push_error(R"(Cannot use "self" inside a static function.)");
}
SelfNode *self = alloc_node<SelfNode>();
+ complete_extents(self);
self->current_class = current_class;
return self;
}
@@ -2204,6 +2304,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token::Type op_type = previous.type;
LiteralNode *constant = alloc_node<LiteralNode>();
+ complete_extents(constant);
switch (op_type) {
case GDScriptTokenizer::Token::CONST_PI:
@@ -2264,30 +2365,38 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
}
break;
default:
+ complete_extents(operation);
return nullptr; // Unreachable.
}
+ complete_extents(operation);
return operation;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
- UnaryOpNode *operation = alloc_node<UnaryOpNode>();
operation->operation = UnaryOpNode::OP_LOGIC_NOT;
operation->variant_op = Variant::OP_NOT;
operation->operand = in_operation;
+ complete_extents(operation);
return operation;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>();
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
Precedence precedence = (Precedence)(get_rule(op.type)->precedence + 1);
operation->left_operand = p_previous_operand;
operation->right_operand = parse_precedence(precedence, false);
+ complete_extents(operation);
if (operation->right_operand == nullptr) {
push_error(vformat(R"(Expected expression after "%s" operator.")", op.get_name()));
@@ -2390,8 +2499,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(Expression
GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
// Only one ternary operation exists, so no abstraction here.
TernaryOpNode *operation = alloc_node<TernaryOpNode>();
- operation->true_expr = p_previous_operand;
+ reset_extents(operation, p_previous_operand);
+ update_extents(operation);
+ operation->true_expr = p_previous_operand;
operation->condition = parse_precedence(PREC_TERNARY, false);
if (operation->condition == nullptr) {
@@ -2406,6 +2517,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio
push_error(R"(Expected expression after "else".)");
}
+ complete_extents(operation);
return operation;
}
@@ -2458,6 +2570,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
AssignmentNode *assignment = alloc_node<AssignmentNode>();
+ reset_extents(assignment, p_previous_operand);
+ update_extents(assignment);
+
make_completion_context(COMPLETION_ASSIGN, assignment);
#ifdef DEBUG_ENABLED
bool has_operator = true;
@@ -2522,6 +2637,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
if (assignment->assigned_value == nullptr) {
push_error(R"(Expected an expression after "=".)");
}
+ complete_extents(assignment);
#ifdef DEBUG_ENABLED
if (source_variable != nullptr) {
@@ -2543,6 +2659,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr
push_error(R"(Expected signal or coroutine after "await".)");
}
await->to_await = element;
+ complete_extents(await);
if (current_function) { // Might be null in a getter or setter.
current_function->is_coroutine = true;
@@ -2571,6 +2688,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_array(ExpressionNode *p_pr
}
pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after array elements.)");
+ complete_extents(array);
return array;
}
@@ -2662,6 +2780,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
}
pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" after dictionary elements.)");
+ complete_extents(dictionary);
return dictionary;
}
@@ -2679,6 +2798,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_grouping(ExpressionNode *p
GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *attribute = alloc_node<SubscriptNode>();
+ reset_extents(attribute, p_previous_operand);
+ update_extents(attribute);
if (for_completion) {
bool is_builtin = false;
@@ -2698,17 +2819,21 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
attribute->base = p_previous_operand;
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
+ complete_extents(attribute);
return attribute;
}
attribute->is_attribute = true;
attribute->attribute = parse_identifier();
+ complete_extents(attribute);
return attribute;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign) {
SubscriptNode *subscript = alloc_node<SubscriptNode>();
+ reset_extents(subscript, p_previous_operand);
+ update_extents(subscript);
make_completion_context(COMPLETION_SUBSCRIPT, subscript);
@@ -2721,15 +2846,19 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
+ complete_extents(subscript);
return subscript;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_previous_operand, bool p_can_assign) {
CastNode *cast = alloc_node<CastNode>();
+ reset_extents(cast, p_previous_operand);
+ update_extents(cast);
cast->operand = p_previous_operand;
cast->cast_type = parse_type();
+ complete_extents(cast);
if (cast->cast_type == nullptr) {
push_error(R"(Expected type specifier after "as".)");
@@ -2741,6 +2870,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_cast(ExpressionNode *p_pre
GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_previous_operand, bool p_can_assign) {
CallNode *call = alloc_node<CallNode>();
+ reset_extents(call, p_previous_operand);
if (previous.type == GDScriptTokenizer::Token::SUPER) {
// Super call.
@@ -2751,6 +2881,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
if (current_function == nullptr) {
push_error(R"(Cannot use implicit "super" call outside of a function.)");
pop_multiline();
+ complete_extents(call);
return nullptr;
}
if (current_function->identifier) {
@@ -2763,6 +2894,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
make_completion_context(COMPLETION_SUPER_METHOD, call, true);
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after ".".)")) {
pop_multiline();
+ complete_extents(call);
return nullptr;
}
IdentifierNode *identifier = parse_identifier();
@@ -2819,56 +2951,108 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
+ complete_extents(call);
return call;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
- if (match(GDScriptTokenizer::Token::LITERAL)) {
- if (previous.literal.get_type() != Variant::STRING) {
- push_error(R"(Expect node path as string or identifier after "$".)");
+ if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
+ return nullptr;
+ }
+
+ if (check(GDScriptTokenizer::Token::LITERAL)) {
+ if (current.literal.get_type() != Variant::STRING) {
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
return nullptr;
}
- GetNodeNode *get_node = alloc_node<GetNodeNode>();
- make_completion_context(COMPLETION_GET_NODE, get_node);
- get_node->string = parse_literal();
- return get_node;
- } else if (current.is_node_name()) {
- GetNodeNode *get_node = alloc_node<GetNodeNode>();
- int chain_position = 0;
- do {
- make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
- if (!current.is_node_name()) {
- push_error(R"(Expect node path after "/".)");
+ }
+
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+
+ // Store the last item in the path so the parser knows what to expect.
+ // Allow allows more specific error messages.
+ enum PathState {
+ PATH_STATE_START,
+ PATH_STATE_SLASH,
+ PATH_STATE_PERCENT,
+ PATH_STATE_NODE_NAME,
+ } path_state = PATH_STATE_START;
+
+ if (previous.type == GDScriptTokenizer::Token::DOLLAR) {
+ // Detect initial slash, which will be handled in the loop if it matches.
+ match(GDScriptTokenizer::Token::SLASH);
+#ifdef DEBUG_ENABLED
+ } else {
+ get_node->use_dollar = false;
+#endif
+ }
+
+ int context_argument = 0;
+
+ do {
+ if (previous.type == GDScriptTokenizer::Token::PERCENT) {
+ if (path_state != PATH_STATE_START && path_state != PATH_STATE_SLASH) {
+ push_error(R"("%" is only valid in the beginning of a node name (either after "$" or after "/"))");
+ complete_extents(get_node);
return nullptr;
}
- advance();
- IdentifierNode *identifier = alloc_node<IdentifierNode>();
- identifier->name = previous.get_identifier();
- get_node->chain.push_back(identifier);
- } while (match(GDScriptTokenizer::Token::SLASH));
- return get_node;
- } else if (match(GDScriptTokenizer::Token::SLASH)) {
- GetNodeNode *get_node = alloc_node<GetNodeNode>();
- IdentifierNode *identifier_root = alloc_node<IdentifierNode>();
- get_node->chain.push_back(identifier_root);
- int chain_position = 0;
- do {
- make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
- if (!current.is_node_name()) {
- push_error(R"(Expect node path after "/".)");
+ get_node->full_path += "%";
+
+ path_state = PATH_STATE_PERCENT;
+ } else if (previous.type == GDScriptTokenizer::Token::SLASH) {
+ if (path_state != PATH_STATE_START && path_state != PATH_STATE_NODE_NAME) {
+ push_error(R"("/" is only valid at the beginning of the path or after a node name.)");
+ complete_extents(get_node);
return nullptr;
}
+
+ get_node->full_path += "/";
+
+ path_state = PATH_STATE_SLASH;
+ }
+
+ make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++);
+
+ if (match(GDScriptTokenizer::Token::LITERAL)) {
+ if (previous.literal.get_type() != Variant::STRING) {
+ String previous_token;
+ switch (path_state) {
+ case PATH_STATE_START:
+ previous_token = "$";
+ break;
+ case PATH_STATE_PERCENT:
+ previous_token = "%";
+ break;
+ case PATH_STATE_SLASH:
+ previous_token = "/";
+ break;
+ default:
+ break;
+ }
+ push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous_token));
+ complete_extents(get_node);
+ return nullptr;
+ }
+
+ get_node->full_path += previous.literal.operator String();
+
+ path_state = PATH_STATE_NODE_NAME;
+ } else if (current.is_node_name()) {
advance();
- IdentifierNode *identifier = alloc_node<IdentifierNode>();
- identifier->name = previous.get_identifier();
- get_node->chain.push_back(identifier);
- } while (match(GDScriptTokenizer::Token::SLASH));
- return get_node;
- } else {
- push_error(R"(Expect node path as string or identifier after "$".)");
- return nullptr;
- }
+ get_node->full_path += previous.get_identifier();
+
+ path_state = PATH_STATE_NODE_NAME;
+ } else if (!check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
+ push_error(vformat(R"(Unexpected "%s" in node path.)", current.get_name()));
+ complete_extents(get_node);
+ return nullptr;
+ }
+ } while (match(GDScriptTokenizer::Token::SLASH) || match(GDScriptTokenizer::Token::PERCENT));
+
+ complete_extents(get_node);
+ return get_node;
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_previous_operand, bool p_can_assign) {
@@ -2891,6 +3075,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after preload path.)*");
+ complete_extents(preload);
return preload;
}
@@ -2926,6 +3111,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
current_function = function;
SuiteNode *body = alloc_node<SuiteNode>();
+ body->parent_function = current_function;
+ body->parent_block = current_suite;
+
SuiteNode *previous_suite = current_suite;
current_suite = body;
@@ -2937,6 +3125,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
in_lambda = true;
function->body = parse_suite("lambda declaration", body, true);
+ complete_extents(function);
+ complete_extents(lambda);
pop_multiline();
@@ -2981,13 +3171,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
if (!match(GDScriptTokenizer::Token::IDENTIFIER)) {
if (match(GDScriptTokenizer::Token::VOID)) {
if (p_allow_void) {
- TypeNode *void_type = alloc_node<TypeNode>();
+ complete_extents(type);
+ TypeNode *void_type = type;
return void_type;
} else {
push_error(R"("void" is only allowed for a function return type.)");
}
}
// Leave error message to the caller who knows the context.
+ complete_extents(type);
return nullptr;
}
@@ -3000,11 +3192,15 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
type->container_type = parse_type(false); // Don't allow void for array element type.
if (type->container_type == nullptr) {
push_error(R"(Expected type for collection after "[".)");
+ complete_extents(type);
type = nullptr;
} else if (type->container_type->container_type != nullptr) {
push_error("Nested typed collections are not supported.");
}
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
+ if (type != nullptr) {
+ complete_extents(type);
+ }
return type;
}
@@ -3017,6 +3213,7 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
}
}
+ complete_extents(type);
return type;
}
@@ -3053,7 +3250,7 @@ bool GDScriptParser::has_comment(int p_line) {
}
String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
ERR_FAIL_COND_V(!comments.has(p_line), String());
if (p_single_line) {
@@ -3105,7 +3302,7 @@ String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
}
void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
- const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ const HashMap<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
if (!comments.has(p_line)) {
return;
}
@@ -3138,24 +3335,23 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
String title, link; // For tutorials.
String doc_line = comments[line++].comment.trim_prefix("##");
- String striped_line = doc_line.strip_edges();
+ String stripped_line = doc_line.strip_edges();
// Set the read mode.
- if (striped_line.begins_with("@desc:") && p_desc.is_empty()) {
+ if (stripped_line.is_empty() && mode == BRIEF && !p_brief.is_empty()) {
mode = DESC;
- striped_line = striped_line.trim_prefix("@desc:");
- in_codeblock = _in_codeblock(doc_line, in_codeblock);
+ continue;
- } else if (striped_line.begins_with("@tutorial")) {
+ } else if (stripped_line.begins_with("@tutorial")) {
int begin_scan = String("@tutorial").length();
- if (begin_scan >= striped_line.length()) {
+ if (begin_scan >= stripped_line.length()) {
continue; // invalid syntax.
}
- if (striped_line[begin_scan] == ':') { // No title.
+ if (stripped_line[begin_scan] == ':') { // No title.
// Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
title = "";
- link = striped_line.trim_prefix("@tutorial:").strip_edges();
+ link = stripped_line.trim_prefix("@tutorial:").strip_edges();
} else {
/* Syntax:
@@ -3163,35 +3359,35 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
* ^ open ^ close ^ colon ^ url
*/
int open_bracket_pos = begin_scan, close_bracket_pos = 0;
- while (open_bracket_pos < striped_line.length() && (striped_line[open_bracket_pos] == ' ' || striped_line[open_bracket_pos] == '\t')) {
+ while (open_bracket_pos < stripped_line.length() && (stripped_line[open_bracket_pos] == ' ' || stripped_line[open_bracket_pos] == '\t')) {
open_bracket_pos++;
}
- if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') {
+ if (open_bracket_pos == stripped_line.length() || stripped_line[open_bracket_pos++] != '(') {
continue; // invalid syntax.
}
close_bracket_pos = open_bracket_pos;
- while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') {
+ while (close_bracket_pos < stripped_line.length() && stripped_line[close_bracket_pos] != ')') {
close_bracket_pos++;
}
- if (close_bracket_pos == striped_line.length()) {
+ if (close_bracket_pos == stripped_line.length()) {
continue; // invalid syntax.
}
int colon_pos = close_bracket_pos + 1;
- while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) {
+ while (colon_pos < stripped_line.length() && (stripped_line[colon_pos] == ' ' || stripped_line[colon_pos] == '\t')) {
colon_pos++;
}
- if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') {
+ if (colon_pos == stripped_line.length() || stripped_line[colon_pos++] != ':') {
continue; // invalid syntax.
}
- title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
- link = striped_line.substr(colon_pos).strip_edges();
+ title = stripped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
+ link = stripped_line.substr(colon_pos).strip_edges();
}
mode = TUTORIALS;
in_codeblock = false;
- } else if (striped_line.is_empty()) {
+ } else if (stripped_line.is_empty()) {
continue;
} else {
// Tutorial docs are single line, we need a @tag after it.
@@ -3211,7 +3407,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
}
doc_line = doc_line.substr(i);
} else {
- doc_line = striped_line;
+ doc_line = stripped_line;
}
String line_join = (in_codeblock) ? "\n" : " ";
@@ -3226,7 +3422,16 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
p_tutorials.append(Pair<String, String>(title, link));
break;
case DONE:
- return;
+ break;
+ }
+ }
+ if (current_class->members.size() > 0) {
+ const ClassNode::Member &m = current_class->members[0];
+ int first_member_line = m.get_line();
+ if (first_member_line == line) {
+ p_brief = "";
+ p_desc = "";
+ p_tutorials.clear();
}
}
}
@@ -3270,7 +3475,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_POWER }, // STAR_STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
- { nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
+ { &GDScriptParser::parse_get_node, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
// Assignment
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // EQUAL,
{ nullptr, &GDScriptParser::parse_assignment, PREC_ASSIGNMENT }, // PLUS_EQUAL,
@@ -3551,8 +3756,12 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = export_type.native_type;
+ } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
+ variable->export_info.hint_string = export_type.native_type;
} else {
- push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
return false;
}
break;
@@ -3604,6 +3813,36 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
return true;
}
+template <PropertyUsageFlags t_usage>
+bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+ AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
+
+ annotation->export_info.name = annotation->resolved_arguments[0];
+
+ switch (t_usage) {
+ case PROPERTY_USAGE_CATEGORY: {
+ annotation->export_info.usage = t_usage;
+ } break;
+
+ case PROPERTY_USAGE_GROUP: {
+ annotation->export_info.usage = t_usage;
+ if (annotation->resolved_arguments.size() == 2) {
+ annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ }
+ } break;
+
+ case PROPERTY_USAGE_SUBGROUP: {
+ annotation->export_info.usage = t_usage;
+ if (annotation->resolved_arguments.size() == 2) {
+ annotation->export_info.hint_string = annotation->resolved_arguments[1];
+ }
+ } break;
+ }
+
+ current_class->add_member_group(annotation);
+ return true;
+}
+
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
#ifdef DEBUG_ENABLED
bool has_error = false;
@@ -3801,6 +4040,46 @@ GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() co
return type;
}
+void GDScriptParser::complete_extents(Node *p_node) {
+ while (!nodes_in_progress.is_empty() && nodes_in_progress.back()->get() != p_node) {
+ ERR_PRINT("Parser bug: Mismatch in extents tracking stack.");
+ nodes_in_progress.pop_back();
+ }
+ if (nodes_in_progress.is_empty()) {
+ ERR_PRINT("Parser bug: Extents tracking stack is empty.");
+ } else {
+ nodes_in_progress.pop_back();
+ }
+}
+
+void GDScriptParser::update_extents(Node *p_node) {
+ p_node->end_line = previous.end_line;
+ p_node->end_column = previous.end_column;
+ p_node->leftmost_column = MIN(p_node->leftmost_column, previous.leftmost_column);
+ p_node->rightmost_column = MAX(p_node->rightmost_column, previous.rightmost_column);
+}
+
+void GDScriptParser::reset_extents(Node *p_node, GDScriptTokenizer::Token p_token) {
+ p_node->start_line = p_token.start_line;
+ p_node->end_line = p_token.end_line;
+ p_node->start_column = p_token.start_column;
+ p_node->end_column = p_token.end_column;
+ p_node->leftmost_column = p_token.leftmost_column;
+ p_node->rightmost_column = p_token.rightmost_column;
+}
+
+void GDScriptParser::reset_extents(Node *p_node, Node *p_from) {
+ if (p_from == nullptr) {
+ return;
+ }
+ p_node->start_line = p_from->start_line;
+ p_node->end_line = p_from->end_line;
+ p_node->start_column = p_from->start_column;
+ p_node->end_column = p_from->end_column;
+ p_node->leftmost_column = p_from->leftmost_column;
+ p_node->rightmost_column = p_from->rightmost_column;
+}
+
/*---------- PRETTY PRINT FOR DEBUG ----------*/
#ifdef DEBUG_ENABLED
@@ -4087,6 +4366,8 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
break;
case ClassNode::Member::ENUM_VALUE:
break; // Nothing. Will be printed by enum.
+ case ClassNode::Member::GROUP:
+ break; // Nothing. Groups are only used by inspector.
case ClassNode::Member::UNDEFINED:
push_line("<unknown member>");
break;
@@ -4250,17 +4531,10 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const
}
void GDScriptParser::TreePrinter::print_get_node(GetNodeNode *p_get_node) {
- push_text("$");
- if (p_get_node->string != nullptr) {
- print_literal(p_get_node->string);
- } else {
- for (int i = 0; i < p_get_node->chain.size(); i++) {
- if (i > 0) {
- push_text("/");
- }
- print_identifier(p_get_node->chain[i]);
- }
+ if (p_get_node->use_dollar) {
+ push_text("$");
}
+ push_text(p_get_node->full_path);
}
void GDScriptParser::TreePrinter::print_identifier(IdentifierNode *p_identifier) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 857e06440c..9c97f98fbc 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -39,7 +39,7 @@
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
#include "core/templates/list.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
#include "gdscript_cache.h"
@@ -325,6 +325,7 @@ public:
Vector<Variant> resolved_arguments;
AnnotationInfo *info = nullptr;
+ PropertyInfo export_info;
bool apply(GDScriptParser *p_this, Node *p_target) const;
bool applies_to(uint32_t p_target_kinds) const;
@@ -500,6 +501,7 @@ public:
VARIABLE,
ENUM,
ENUM_VALUE, // For unnamed enums.
+ GROUP, // For member grouping.
};
Type type = UNDEFINED;
@@ -511,6 +513,7 @@ public:
SignalNode *signal;
VariableNode *variable;
EnumNode *m_enum;
+ AnnotationNode *annotation;
};
EnumNode::Value enum_value;
@@ -532,6 +535,8 @@ public:
return "enum";
case ENUM_VALUE:
return "enum value";
+ case GROUP:
+ return "group";
}
return "";
}
@@ -552,6 +557,8 @@ public:
return m_enum->start_line;
case SIGNAL:
return signal->start_line;
+ case GROUP:
+ return annotation->start_line;
case UNDEFINED:
ERR_FAIL_V_MSG(-1, "Reaching undefined member type.");
}
@@ -586,6 +593,9 @@ public:
// TODO: Add parameter info.
return type;
}
+ case GROUP: {
+ return DataType();
+ }
case UNDEFINED:
return DataType();
}
@@ -622,6 +632,10 @@ public:
type = ENUM_VALUE;
enum_value = p_enum_value;
}
+ Member(AnnotationNode *p_annotation) {
+ type = GROUP;
+ annotation = p_annotation;
+ }
};
IdentifierNode *identifier = nullptr;
@@ -668,6 +682,10 @@ public:
members_indices[p_enum_value.identifier->name] = members.size();
members.push_back(Member(p_enum_value));
}
+ void add_member_group(AnnotationNode *p_annotation_node) {
+ members_indices[p_annotation_node->export_info.name] = members.size();
+ members.push_back(Member(p_annotation_node));
+ }
ClassNode() {
type = CLASS;
@@ -749,8 +767,10 @@ public:
};
struct GetNodeNode : public ExpressionNode {
- LiteralNode *string = nullptr;
- Vector<IdentifierNode *> chain;
+ String full_path;
+#ifdef DEBUG_ENABLED
+ bool use_dollar = true;
+#endif
GetNodeNode() {
type = GET_NODE;
@@ -802,7 +822,7 @@ public:
FunctionNode *function = nullptr;
FunctionNode *parent_function = nullptr;
Vector<IdentifierNode *> captures;
- Map<StringName, int> captures_indices;
+ HashMap<StringName, int> captures_indices;
bool use_self = false;
bool has_name() const {
@@ -1205,9 +1225,9 @@ private:
List<ParserError> errors;
#ifdef DEBUG_ENABLED
List<GDScriptWarning> warnings;
- Set<String> ignored_warnings;
- Set<uint32_t> ignored_warning_codes;
- Set<int> unsafe_lines;
+ HashSet<String> ignored_warnings;
+ HashSet<uint32_t> ignored_warning_codes;
+ HashSet<int> unsafe_lines;
#endif
GDScriptTokenizer tokenizer;
@@ -1236,6 +1256,7 @@ private:
SIGNAL = 1 << 4,
FUNCTION = 1 << 5,
STATEMENT = 1 << 6,
+ STANDALONE = 1 << 7,
CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
};
uint32_t target_kind = 0; // Flags.
@@ -1280,6 +1301,12 @@ private:
};
static ParseRule *get_rule(GDScriptTokenizer::Token::Type p_token_type);
+ List<Node *> nodes_in_progress;
+ void complete_extents(Node *p_node);
+ void update_extents(Node *p_node);
+ void reset_extents(Node *p_node, GDScriptTokenizer::Token p_token);
+ void reset_extents(Node *p_node, Node *p_from);
+
template <class T>
T *alloc_node() {
T *node = memnew(T);
@@ -1287,13 +1314,8 @@ private:
node->next = list;
list = node;
- // TODO: Properly set positions for all nodes.
- node->start_line = previous.start_line;
- node->end_line = previous.end_line;
- node->start_column = previous.start_column;
- node->end_column = previous.end_column;
- node->leftmost_column = previous.leftmost_column;
- node->rightmost_column = previous.rightmost_column;
+ reset_extents(node, previous);
+ nodes_in_progress.push_back(node);
return node;
}
@@ -1338,7 +1360,7 @@ private:
SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
- bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
+ bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
bool validate_annotation_arguments(AnnotationNode *p_annotation);
void clear_unused_annotations();
bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
@@ -1346,6 +1368,8 @@ private:
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
+ template <PropertyUsageFlags t_usage>
+ bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
template <Multiplayer::RPCMode t_mode>
bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
@@ -1411,6 +1435,7 @@ public:
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
void get_annotation_list(List<MethodInfo> *r_annotations) const;
+ bool annotation_exists(const String &p_annotation_name) const;
const List<ParserError> &get_errors() const { return errors; }
const List<String> get_dependencies() const {
@@ -1419,7 +1444,7 @@ public:
}
#ifdef DEBUG_ENABLED
const List<GDScriptWarning> &get_warnings() const { return warnings; }
- const Set<int> &get_unsafe_lines() const { return unsafe_lines; }
+ const HashSet<int> &get_unsafe_lines() const { return unsafe_lines; }
int get_last_line_number() const { return current.end_line; }
#endif
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index 07ef5aefcb..63ebd8acf5 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -71,7 +71,7 @@ GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_m
object = p_object;
method = p_method;
h = method.hash();
- h = hash_djb2_one_64(object->get_instance_id(), h);
+ h = hash_murmur3_one_64(object->get_instance_id(), h);
node = Object::cast_to<Node>(object);
ERR_FAIL_COND_MSG(!node, "RPC can only be defined on class that extends Node.");
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 75f9a7626e..7fb715f2c8 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -31,9 +31,9 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
+#include "core/templates/hash_map.h"
+#include "core/templates/hash_set.h"
#include "core/templates/list.h"
-#include "core/templates/map.h"
-#include "core/templates/set.h"
#include "core/templates/vector.h"
#include "core/variant/variant.h"
@@ -193,7 +193,7 @@ public:
new_line = p_new_line;
}
};
- const Map<int, CommentData> &get_comments() const {
+ const HashMap<int, CommentData> &get_comments() const {
return comments;
}
#endif // TOOLS_ENABLED
@@ -226,7 +226,7 @@ private:
int length = 0;
#ifdef TOOLS_ENABLED
- Map<int, CommentData> comments;
+ HashMap<int, CommentData> comments;
#endif // TOOLS_ENABLED
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index e28dd26c28..e0beed367a 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -177,6 +177,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
err_text = "Invalid call. Nonexistent " + p_where + ".";
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ err_text = "Attempt to call " + p_where + " on a const instance.";
} else {
err_text = "Bug, call error: #" + itos(p_err.error);
}
@@ -311,6 +313,7 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
&&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_JUMP_IF_SHARED, \
&&OPCODE_RETURN, \
&&OPCODE_RETURN_TYPED_BUILTIN, \
&&OPCODE_RETURN_TYPED_ARRAY, \
@@ -546,33 +549,32 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[i], Variant);
}
- memnew_placement(&stack[ADDR_STACK_NIL], Variant);
-
if (_instruction_args_size) {
instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
} else {
instruction_args = nullptr;
}
- if (p_instance) {
- memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
- script = p_instance->script.ptr();
- } else {
- memnew_placement(&stack[ADDR_STACK_SELF], Variant);
- script = _script;
+ for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
+ type_init_function_table[E.value](&stack[E.key]);
}
}
+
if (_ptrcall_args_size) {
call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
} else {
call_args_ptr = nullptr;
}
- memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
-
- for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
- type_init_function_table[E.value](&stack[E.key]);
+ if (p_instance) {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
+ script = p_instance->script.ptr();
+ } else {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant);
+ script = _script;
}
+ memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
+ memnew_placement(&stack[ADDR_STACK_NIL], Variant);
String err_text;
@@ -1030,11 +1032,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#endif
#ifdef DEBUG_ENABLED
if (!valid) {
- if (src->has_method(*index)) {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
- } else {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
- }
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
OPCODE_BREAK;
}
*dst = ret;
@@ -1895,7 +1893,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
VariantInternal::initialize(ret, Variant::OBJECT);
Object **ret_opaque = VariantInternal::get_object(ret);
method->ptrcall(base_obj, argptrs, ret_opaque);
- VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too.
+ VariantInternal::update_object_id(ret);
#ifdef DEBUG_ENABLED
if (GDScriptLanguage::get_singleton()->profiling) {
@@ -2104,7 +2102,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
const GDScript *gds = _script;
- const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E;
while (gds->base.ptr()) {
gds = gds->base.ptr();
E = gds->member_functions.find(*methodname);
@@ -2116,7 +2114,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err;
if (E) {
- *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+ *dst = E->value->call(p_instance, (const Variant **)argptrs, argc, err);
} else if (gds->native.ptr()) {
if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
@@ -2171,8 +2169,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
// Is this even possible to be null at this point?
if (obj) {
if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- static StringName completed = _scs_create("completed");
- result = Signal(obj, completed);
+ result = Signal(obj, "completed");
}
}
}
@@ -2193,8 +2190,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
gdfs->function = this;
gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
+
+ // First 3 stack addresses are special, so we just skip them here.
+ for (int i = 3; i < _stack_size; i++) {
memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
}
gdfs->state.stack_size = _stack_size;
@@ -2362,6 +2360,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_JUMP_IF_SHARED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(val, 0);
+
+ if (val->is_shared()) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_RETURN) {
CHECK_SPACE(2);
GET_INSTRUCTION_ARG(r, 0);
@@ -3433,9 +3446,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
_err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
}
-#endif
// Get a default return type in case of failure
retvalue = _get_default_variant_for_data_type(return_type);
+#endif
OPCODE_OUT;
}
@@ -3451,26 +3464,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
}
- // Check if this is the last time the function is resuming from await
- // Will be true if never awaited as well
- // When it's the last resume it will postpone the exit from stack,
- // so the debugger knows which function triggered the resume of the next function (if any)
+ // Check if this is not the last time it was interrupted by `await` or if it's the first time executing.
+ // If that is the case then we exit the function as normal. Otherwise we postpone it until the last `await` is completed.
+ // This ensures the call stack can be properly shown when using `await`, showing what resumed the function.
if (!p_state || awaited) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->exit_function();
}
#endif
- if (_stack_size) {
- //free stack
- for (int i = 0; i < _stack_size; i++) {
- stack[i].~Variant();
- }
+ // Free stack, except reserved addresses.
+ for (int i = 3; i < _stack_size; i++) {
+ stack[i].~Variant();
}
-
#ifdef DEBUG_ENABLED
}
#endif
+ // Always free reserved addresses, since they are never copied.
+ for (int i = 0; i < 3; i++) {
+ stack[i].~Variant();
+ }
+
return retvalue;
}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index ad96e36640..1cae7bdfac 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -163,6 +163,18 @@ String GDScriptWarning::get_message() const {
#undef CHECK_SYMBOLS
}
+int GDScriptWarning::get_default_value(Code p_code) {
+ if (get_name_from_code(p_code).to_lower().begins_with("unsafe_")) {
+ return WarnLevel::IGNORE;
+ }
+ return WarnLevel::WARN;
+}
+
+PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
+ // Making this a separate function in case a warning needs different PropertyInfo in the future.
+ return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
+}
+
String GDScriptWarning::get_name() const {
return get_name_from_code(code);
}
@@ -210,6 +222,10 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
return names[(int)p_code];
}
+String GDScriptWarning::get_settings_path_from_code(Code p_code) {
+ return "debug/gdscript/warnings/" + get_name_from_code(p_code).to_lower();
+}
+
GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) {
for (int i = 0; i < WARNING_MAX; i++) {
if (get_name_from_code((Code)i) == p_name) {
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 82efe3568f..f47f31aedf 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -33,11 +33,18 @@
#ifdef DEBUG_ENABLED
+#include "core/object/object.h"
#include "core/string/ustring.h"
#include "core/templates/vector.h"
class GDScriptWarning {
public:
+ enum WarnLevel {
+ IGNORE,
+ WARN,
+ ERROR
+ };
+
enum Code {
UNASSIGNED_VARIABLE, // Variable used but never assigned.
UNASSIGNED_VARIABLE_OP_ASSIGN, // Variable never assigned but used in an assignment operation (+=, *=, etc).
@@ -81,7 +88,10 @@ public:
String get_name() const;
String get_message() const;
+ static int get_default_value(Code p_code);
+ static PropertyInfo get_property_info(Code p_code);
static String get_name_from_code(Code p_code);
+ static String get_settings_path_from_code(Code p_code);
static Code get_code_from_name(const String &p_name);
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index bc1f001d86..03e93821c7 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -216,8 +216,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
if (res.is_valid() && !res->get_path().is_empty()) {
value_text = "preload(\"" + res->get_path() + "\")";
if (symbol.documentation.is_empty()) {
- if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
- symbol.documentation = S->get()->class_symbol.documentation;
+ if (HashMap<String, ExtendGDScriptParser *>::Iterator S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
+ symbol.documentation = S->value->class_symbol.documentation;
}
}
} else {
@@ -307,6 +307,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
parse_class_symbol(m.m_class, symbol);
r_symbol.children.push_back(symbol);
} break;
+ case ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case ClassNode::Member::UNDEFINED:
break; // Unreachable.
}
@@ -815,6 +817,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
methods.append(dump_function_api(m.function));
}
} break;
+ case ClassNode::Member::GROUP:
+ break; // No-op, but silences warnings.
case ClassNode::Member::UNDEFINED:
break; // Unreachable.
}
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 1f02943480..5ad9680ea0 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -141,9 +141,9 @@ Array GDScriptTextDocument::documentSymbol(const Dictionary &p_params) {
String uri = params["uri"];
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(uri);
Array arr;
- if (const Map<String, ExtendGDScriptParser *>::Element *parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
+ if (HashMap<String, ExtendGDScriptParser *>::ConstIterator parser = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(path)) {
Vector<lsp::DocumentedSymbolInformation> list;
- parser->get()->get_symbols().symbol_tree_as_list(uri, list);
+ parser->value->get_symbols().symbol_tree_as_list(uri, list);
for (int i = 0; i < list.size(); i++) {
arr.push_back(list[i].to_json());
}
@@ -169,6 +169,7 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
lsp::CompletionItem item;
item.label = option.display;
item.data = request_data;
+ item.insertText = option.insert_text;
switch (option.kind) {
case ScriptLanguage::CODE_COMPLETION_KIND_ENUM:
@@ -267,8 +268,8 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
}
if (!symbol) {
- if (const Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
- symbol = E->get()->get_member_symbol(member_name, inner_class_name);
+ if (HashMap<String, ExtendGDScriptParser *>::ConstIterator E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(class_name)) {
+ symbol = E->value->get_member_symbol(member_name, inner_class_name);
}
}
}
@@ -278,12 +279,7 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
item.documentation = symbol->render();
}
- if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
- item.insertText = item.label + "(";
- if (symbol && symbol->children.is_empty()) {
- item.insertText += ")";
- }
- } else if (item.kind == lsp::CompletionItemKind::Event) {
+ if (item.kind == lsp::CompletionItemKind::Event) {
if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) {
const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
item.insertText = item.label.quote(quote_style);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 378dc6d04b..8d484a43b3 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -116,22 +116,22 @@ void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
}
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
- Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path);
- Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator parser = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator script = scripts.find(p_path);
if (parser && script) {
- if (script->get() && script->get() == parser->get()) {
- memdelete(script->get());
+ if (script->value && script->value == parser->value) {
+ memdelete(script->value);
} else {
- memdelete(script->get());
- memdelete(parser->get());
+ memdelete(script->value);
+ memdelete(parser->value);
}
parse_results.erase(p_path);
scripts.erase(p_path);
} else if (parser) {
- memdelete(parser->get());
+ memdelete(parser->value);
parse_results.erase(p_path);
} else if (script) {
- memdelete(script->get());
+ memdelete(script->value);
scripts.erase(p_path);
}
}
@@ -141,8 +141,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
StringName empty;
while (class_name != empty) {
- if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
- const lsp::DocumentSymbol &class_symbol = E->value();
+ if (HashMap<StringName, lsp::DocumentSymbol>::ConstIterator E = native_symbols.find(class_name)) {
+ const lsp::DocumentSymbol &class_symbol = E->value;
if (p_member.is_empty()) {
return &class_symbol;
@@ -162,9 +162,9 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
}
const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_path) const {
- const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::ConstIterator S = scripts.find(p_path);
if (S) {
- return &(S->get()->get_symbols());
+ return &(S->value->get_symbols());
}
return nullptr;
}
@@ -209,10 +209,10 @@ void GDScriptWorkspace::reload_all_workspace_scripts() {
err = parse_script(path, content);
if (err != OK) {
- Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(path);
String err_msg = "Failed parse script " + path;
if (S) {
- err_msg += "\n" + S->get()->get_errors()[0].message;
+ err_msg += "\n" + S->value->get_errors()[0].message;
}
ERR_CONTINUE_MSG(err != OK, err_msg);
}
@@ -238,25 +238,25 @@ void GDScriptWorkspace::list_script_files(const String &p_root_dir, List<String>
}
ExtendGDScriptParser *GDScriptWorkspace::get_parse_successed_script(const String &p_path) {
- const Map<String, ExtendGDScriptParser *>::Element *S = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = scripts.find(p_path);
if (!S) {
parse_local_script(p_path);
S = scripts.find(p_path);
}
if (S) {
- return S->get();
+ return S->value;
}
return nullptr;
}
ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path) {
- const Map<String, ExtendGDScriptParser *>::Element *S = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator S = parse_results.find(p_path);
if (!S) {
parse_local_script(p_path);
S = parse_results.find(p_path);
}
if (S) {
- return S->get();
+ return S->value;
}
return nullptr;
}
@@ -424,8 +424,8 @@ Error GDScriptWorkspace::initialize() {
Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_content) {
ExtendGDScriptParser *parser = memnew(ExtendGDScriptParser);
Error err = parser->parse(p_content, p_path);
- Map<String, ExtendGDScriptParser *>::Element *last_parser = parse_results.find(p_path);
- Map<String, ExtendGDScriptParser *>::Element *last_script = scripts.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator last_parser = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::Iterator last_script = scripts.find(p_path);
if (err == OK) {
remove_cache_parser(p_path);
@@ -433,8 +433,8 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
scripts[p_path] = parser;
} else {
- if (last_parser && last_script && last_parser->get() != last_script->get()) {
- memdelete(last_parser->get());
+ if (last_parser && last_script && last_parser->value != last_script->value) {
+ memdelete(last_parser->value);
}
parse_results[p_path] = parser;
}
@@ -513,9 +513,9 @@ String GDScriptWorkspace::get_file_uri(const String &p_path) const {
void GDScriptWorkspace::publish_diagnostics(const String &p_path) {
Dictionary params;
Array errors;
- const Map<String, ExtendGDScriptParser *>::Element *ele = parse_results.find(p_path);
+ HashMap<String, ExtendGDScriptParser *>::ConstIterator ele = parse_results.find(p_path);
if (ele) {
- const Vector<lsp::Diagnostic> &list = ele->get()->get_diagnostics();
+ const Vector<lsp::Diagnostic> &list = ele->value->get_diagnostics();
errors.resize(list.size());
for (int i = 0; i < list.size(); ++i) {
errors[i] = list[i].to_json();
@@ -707,8 +707,8 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
}
const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
- if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
- const lsp::DocumentSymbol &symbol = E->get();
+ if (HashMap<StringName, lsp::DocumentSymbol>::Iterator E = native_symbols.find(p_params.native_class)) {
+ const lsp::DocumentSymbol &symbol = E->value;
if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {
return &symbol;
}
@@ -784,7 +784,7 @@ GDScriptWorkspace::GDScriptWorkspace() {
}
GDScriptWorkspace::~GDScriptWorkspace() {
- Set<String> cached_parsers;
+ HashSet<String> cached_parsers;
for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) {
cached_parsers.insert(E.key);
@@ -794,7 +794,7 @@ GDScriptWorkspace::~GDScriptWorkspace() {
cached_parsers.insert(E.key);
}
- for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) {
- remove_cache_parser(E->get());
+ for (const String &E : cached_parsers) {
+ remove_cache_parser(E);
}
}
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 92e78f8992..7bff5db81f 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -48,7 +48,7 @@ protected:
static void _bind_methods();
void remove_cache_parser(const String &p_path);
bool initialized = false;
- Map<StringName, lsp::DocumentSymbol> native_symbols;
+ HashMap<StringName, lsp::DocumentSymbol> native_symbols;
const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const;
const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const;
@@ -68,8 +68,8 @@ public:
String root;
String root_uri;
- Map<String, ExtendGDScriptParser *> scripts;
- Map<String, ExtendGDScriptParser *> parse_results;
+ HashMap<String, ExtendGDScriptParser *> scripts;
+ HashMap<String, ExtendGDScriptParser *> parse_results;
HashMap<StringName, ClassMembers> native_members;
public:
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index a63f9df918..1c9349097f 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -261,7 +261,7 @@ struct WorkspaceEdit {
/**
* Holds changes to existing resources.
*/
- Map<String, Vector<TextEdit>> changes;
+ HashMap<String, Vector<TextEdit>> changes;
_FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) {
if (changes.has(uri)) {
@@ -293,8 +293,8 @@ struct WorkspaceEdit {
}
_FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) {
- if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
- Vector<TextEdit> edit_list = E->value();
+ if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) {
+ Vector<TextEdit> edit_list = E->value;
for (int i = 0; i < edit_list.size(); ++i) {
TextEdit edit = edit_list[i];
if (edit.range.start.character == start_character) {
@@ -310,8 +310,8 @@ struct WorkspaceEdit {
new_edit.range.end.line = line;
new_edit.range.end.character = end_character;
- if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
- E->value().push_back(new_edit);
+ if (HashMap<String, Vector<TextEdit>>::Iterator E = changes.find(uri)) {
+ E->value.push_back(new_edit);
} else {
Vector<TextEdit> edit_list;
edit_list.push_back(new_edit);
@@ -1004,8 +1004,8 @@ struct CompletionItem {
dict["label"] = label;
dict["kind"] = kind;
dict["data"] = data;
+ dict["insertText"] = insertText;
if (resolved) {
- dict["insertText"] = insertText;
dict["detail"] = detail;
dict["documentation"] = documentation.to_json();
dict["deprecated"] = deprecated;
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 59acb1c064..b230c6ba36 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -70,7 +70,7 @@ class EditorExportGDScript : public EditorExportPlugin {
GDCLASS(EditorExportGDScript, EditorExportPlugin);
public:
- virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features) override {
+ virtual void _export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) override {
int script_mode = EditorExportPreset::MODE_SCRIPT_COMPILED;
String script_key;
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 71dc5de7e4..ff4832bde0 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -363,7 +363,7 @@ void GDScriptTest::disable_stdout() {
OS::get_singleton()->set_stderr_enabled(false);
}
-void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error) {
+void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich) {
TestResult *result = (TestResult *)p_this;
result->output += p_message + "\n";
}
@@ -543,8 +543,8 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
return result;
}
// Test running.
- const Map<StringName, GDScriptFunction *>::Element *test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
- if (test_function_element == nullptr) {
+ const HashMap<StringName, GDScriptFunction *>::ConstIterator test_function_element = script->get_member_functions().find(GDScriptTestRunner::test_function_name);
+ if (!test_function_element) {
enable_stdout();
result.status = GDTEST_LOAD_ERROR;
result.output = "";
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
index d6c6419e21..ee21afd9c9 100644
--- a/modules/gdscript/tests/gdscript_test_runner.h
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -86,7 +86,7 @@ private:
TestResult execute_test_code(bool p_is_generating);
public:
- static void print_handler(void *p_this, const String &p_message, bool p_error);
+ static void print_handler(void *p_this, const String &p_message, bool p_error, bool p_rich);
static void error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, bool p_editor_notify, ErrorHandlerType p_type);
TestResult run_test();
bool generate_output();
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd
new file mode 100644
index 0000000000..9a7c6a8250
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.gd
@@ -0,0 +1,12 @@
+# https://github.com/godotengine/godot/issues/54589
+# https://github.com/godotengine/godot/issues/56265
+
+extends Resource
+
+func test():
+ print("okay")
+ await self.changed
+ await unknown(self)
+
+func unknown(arg):
+ await arg.changed
diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out
new file mode 100644
index 0000000000..2dc04a363e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/await_with_signals_no_warning.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+okay
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd
new file mode 100644
index 0000000000..9f86d0531c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.gd
@@ -0,0 +1,2 @@
+func test():
+ const arr: Array[int] = ["Hello", "World"]
diff --git a/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
new file mode 100644
index 0000000000..26b6e13d4f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int].
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
index d13d713454..ada6030132 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
@@ -1,6 +1,6 @@
-# Error here. `class_name` should be used *before* annotations, not after.
+# Error here. `class_name` should be used *before* annotations, not after (except @tool).
@icon("res://path/to/optional/icon.svg")
class_name HelloWorld
func test():
- pass
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
index 0bcc8acc55..02b33c8692 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"class_name" should be used before annotations.
+"class_name" should be used before annotations (except @tool).
diff --git a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
+++ b/modules/gdscript/tests/scripts/parser/errors/dollar-assignment-bug-53696.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd
new file mode 100644
index 0000000000..fa0a43094e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.gd
@@ -0,0 +1,3 @@
+func test():
+ func standalone():
+ print("can't be accessed")
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out
new file mode 100644
index 0000000000..c6830c8258
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_standalone.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Standalone lambdas cannot be accessed. Consider assigning it to a variable.
diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd
new file mode 100644
index 0000000000..409da11051
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.gd
@@ -0,0 +1,4 @@
+func test():
+ var TEST = 1
+ for TEST in 2:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out
new file mode 100644
index 0000000000..407f094ca0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_for_variable.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a variable named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd
new file mode 100644
index 0000000000..b353fd1288
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.gd
@@ -0,0 +1,3 @@
+func test():
+ var TEST = 1
+ var TEST = 2
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out
new file mode 100644
index 0000000000..407f094ca0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_variable.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a variable named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
index b3dc181a22..9fafcb5a64 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path as string or identifier after "$".
+Expected node path as string or identifier after "$".
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
index dcb4ccecb0..3062f0be70 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Expect node path after "/".
+Expected node path as string or identifier after "/".
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
new file mode 100644
index 0000000000..f04f4de08d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.gd
@@ -0,0 +1,49 @@
+extends Node
+
+func test():
+ var child = Node.new()
+ child.name = "Child"
+ add_child(child)
+ child.owner = self
+
+ var hey = Node.new()
+ hey.name = "Hey"
+ child.add_child(hey)
+ hey.owner = self
+ hey.unique_name_in_owner = true
+
+ var fake_hey = Node.new()
+ fake_hey.name = "Hey"
+ add_child(fake_hey)
+ fake_hey.owner = self
+
+ var sub_child = Node.new()
+ sub_child.name = "SubChild"
+ hey.add_child(sub_child)
+ sub_child.owner = self
+
+ var howdy = Node.new()
+ howdy.name = "Howdy"
+ sub_child.add_child(howdy)
+ howdy.owner = self
+ howdy.unique_name_in_owner = true
+
+ print(hey == $Child/Hey)
+ print(howdy == $Child/Hey/SubChild/Howdy)
+
+ print(%Hey == hey)
+ print($%Hey == hey)
+ print(%"Hey" == hey)
+ print($"%Hey" == hey)
+ print($%"Hey" == hey)
+ print(%Hey/%Howdy == howdy)
+ print($%Hey/%Howdy == howdy)
+ print($"%Hey/%Howdy" == howdy)
+ print($"%Hey"/"%Howdy" == howdy)
+ print(%"Hey"/"%Howdy" == howdy)
+ print($%"Hey"/"%Howdy" == howdy)
+ print($"%Hey"/%"Howdy" == howdy)
+ print(%"Hey"/%"Howdy" == howdy)
+ print($%"Hey"/%"Howdy" == howdy)
+ print(%"Hey/%Howdy" == howdy)
+ print($%"Hey/%Howdy" == howdy)
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out
new file mode 100644
index 0000000000..041c4439b0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_and_percent_get_node.out
@@ -0,0 +1,19 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd
new file mode 100644
index 0000000000..f5e26ab1ab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.gd
@@ -0,0 +1,7 @@
+# https://github.com/godotengine/godot/issues/61231
+
+func test():
+ var my_lambda = func():
+ print("hello")
+ if 0 == 0:
+ my_lambda.call()
diff --git a/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out
new file mode 100644
index 0000000000..58774d2d3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/if_after_lambda.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+hello
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd
new file mode 100644
index 0000000000..2140b6923e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.gd
@@ -0,0 +1,7 @@
+# https://github.com/godotengine/godot/issues/56751
+
+func test():
+ var x = "local"
+ var lambda = func(param = x):
+ print(param)
+ lambda.call()
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out
new file mode 100644
index 0000000000..ce3241b94d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_default_parameter_capture.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+local
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
new file mode 100644
index 0000000000..707e4532cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.gd
@@ -0,0 +1,13 @@
+# https://github.com/godotengine/godot/pull/61666
+
+func test():
+ var dict := {"key": "value"}
+ match dict:
+ {"key": var value}:
+ print(value) # used, no warning
+ match dict:
+ {"key": var value}:
+ pass # unused, warning
+ match dict:
+ {"key": var _value}:
+ pass # unused, suppressed warning from underscore
diff --git a/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
new file mode 100644
index 0000000000..057c1b11e5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_bind_unused.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 9
+>> UNUSED_VARIABLE
+>> The local variable 'value' is declared but never used in the block. If this is intended, prefix it with an underscore: '_value'
+value
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
new file mode 100644
index 0000000000..377dd25e9e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.gd
@@ -0,0 +1,43 @@
+func foo(x):
+ match x:
+ {"key1": "value1", "key2": "value2"}:
+ print('{"key1": "value1", "key2": "value2"}')
+ {"key1": "value1", "key2"}:
+ print('{"key1": "value1", "key2"}')
+ {"key1", "key2": "value2"}:
+ print('{"key1", "key2": "value2"}')
+ {"key1", "key2"}:
+ print('{"key1", "key2"}')
+ {"key1": "value1"}:
+ print('{"key1": "value1"}')
+ {"key1"}:
+ print('{"key1"}')
+ _:
+ print("wildcard")
+
+func bar(x):
+ match x:
+ {0}:
+ print("0")
+ {1}:
+ print("1")
+ {2}:
+ print("2")
+ _:
+ print("wildcard")
+
+func test():
+ foo({"key1": "value1", "key2": "value2"})
+ foo({"key1": "value1", "key2": ""})
+ foo({"key1": "", "key2": "value2"})
+ foo({"key1": "", "key2": ""})
+ foo({"key1": "value1"})
+ foo({"key1": ""})
+ foo({"key1": "value1", "key2": "value2", "key3": "value3"})
+ foo({"key1": "value1", "key3": ""})
+ foo({"key2": "value2"})
+ foo({"key3": ""})
+ bar({0: "0"})
+ bar({1: "1"})
+ bar({2: "2"})
+ bar({3: "3"})
diff --git a/modules/gdscript/tests/scripts/parser/features/match_dictionary.out b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
new file mode 100644
index 0000000000..4dee886927
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_dictionary.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+{"key1": "value1", "key2": "value2"}
+{"key1": "value1", "key2"}
+{"key1", "key2": "value2"}
+{"key1", "key2"}
+{"key1": "value1"}
+{"key1"}
+wildcard
+wildcard
+wildcard
+wildcard
+0
+1
+2
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
new file mode 100644
index 0000000000..dbe223f5f5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.gd
@@ -0,0 +1,26 @@
+func foo(x):
+ match x:
+ 1, [2]:
+ print('1, [2]')
+ _:
+ print('wildcard')
+
+func bar(x):
+ match x:
+ [1], [2], [3]:
+ print('[1], [2], [3]')
+ [4]:
+ print('[4]')
+ _:
+ print('wildcard')
+
+func test():
+ foo(1)
+ foo([2])
+ foo(2)
+ bar([1])
+ bar([2])
+ bar([3])
+ bar([4])
+ bar([5])
+
diff --git a/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
new file mode 100644
index 0000000000..a12b934d67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match_multiple_patterns_with_array.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+1, [2]
+1, [2]
+wildcard
+[1], [2], [3]
+[1], [2], [3]
+[1], [2], [3]
+[4]
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd
new file mode 100644
index 0000000000..d00d483a73
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.gd
@@ -0,0 +1,16 @@
+func test():
+ var foo := "bar"
+ match foo:
+ "baz":
+ return
+ _:
+ pass
+ match foo:
+ "baz":
+ return
+ match foo:
+ "bar":
+ pass
+ _:
+ return
+ print("reached")
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out
new file mode 100644
index 0000000000..47db6b631b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return_bug_55154.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+reached
diff --git a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd
new file mode 100644
index 0000000000..d2f3a3e18f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.gd
@@ -0,0 +1,19 @@
+func test():
+ var dictionary1: Variant = {1:Vector2()}
+ dictionary1[1].x = 2
+ var dictionary2: Dictionary = {3:Vector2()}
+ dictionary2[3].x = 4
+ var array1: Variant = [[Vector2()]]
+ array1[0][0].x = 5
+ var array2: Array = [[Vector2()]]
+ array2[0][0].x = 6
+ var array3: Array[Array] = [[Vector2()]]
+ array3[0][0].x = 7
+ var transform = Transform3D()
+ transform.basis.x = Vector3(8.0, 9.0, 7.0)
+ print(dictionary1)
+ print(dictionary2)
+ print(array1)
+ print(array2)
+ print(array3)
+ print(transform)
diff --git a/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
new file mode 100644
index 0000000000..5e7ccf534a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/chain_assignment_works.out
@@ -0,0 +1,7 @@
+GDTEST_OK
+{1:(2, 0)}
+{3:(4, 0)}
+[[(5, 0)]]
+[[(6, 0)]]
+[[(7, 0)]]
+[X: (8, 9, 7), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)]
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index d8f60d5e9b..cbcd7b2955 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -134,6 +134,34 @@ static void test_parser(const String &p_code, const String &p_script_path, const
#endif
}
+static void recursively_disassemble_functions(const Ref<GDScript> script, const Vector<String> &p_lines) {
+ for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
+ const GDScriptFunction *func = E.value;
+
+ String signature = "Disassembling " + func->get_name().operator String() + "(";
+ for (int i = 0; i < func->get_argument_count(); i++) {
+ if (i > 0) {
+ signature += ", ";
+ }
+ signature += func->get_argument_name(i);
+ }
+ print_line(signature + ")");
+#ifdef TOOLS_ENABLED
+ func->disassemble(p_lines);
+#endif
+ print_line("");
+ print_line("");
+ }
+
+ for (const KeyValue<StringName, Ref<GDScript>> &F : script->get_subclasses()) {
+ const Ref<GDScript> inner_script = F.value;
+ print_line("");
+ print_line(vformat("Inner Class: %s", inner_script->get_script_class_name()));
+ print_line("");
+ recursively_disassemble_functions(inner_script, p_lines);
+ }
+}
+
static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
GDScriptParser parser;
Error err = parser.parse(p_code, p_script_path, false);
@@ -172,23 +200,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
return;
}
- for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
- const GDScriptFunction *func = E.value;
-
- String signature = "Disassembling " + func->get_name().operator String() + "(";
- for (int i = 0; i < func->get_argument_count(); i++) {
- if (i > 0) {
- signature += ", ";
- }
- signature += func->get_argument_name(i);
- }
- print_line(signature + ")");
-#ifdef TOOLS_ENABLED
- func->disassemble(p_lines);
-#endif
- print_line("");
- print_line("");
- }
+ recursively_disassemble_functions(script, p_lines);
}
void test(TestType p_type) {
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index 64891d9ee8..b1c2140039 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -38,7 +38,8 @@
#include <glslang/Public/ShaderLang.h>
#include <glslang/SPIRV/GlslangToSpv.h>
-static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) {
+static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device) {
+ const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
Vector<uint8_t> ret;
ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret);
@@ -58,12 +59,12 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
glslang::TShader::ForbidIncluder includer;
- if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
- if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) {
+ if (capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
+ if (capabilities->version_major == 1 && capabilities->version_minor == 0) {
ClientVersion = glslang::EShTargetVulkan_1_0;
TargetVersion = glslang::EShTargetSpv_1_0;
check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0
- } else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) {
+ } else if (capabilities->version_major == 1 && capabilities->version_minor == 1) {
ClientVersion = glslang::EShTargetVulkan_1_1;
TargetVersion = glslang::EShTargetSpv_1_3;
} else {
@@ -90,34 +91,36 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
if (check_subgroup_support) {
uint32_t stage_bit = 1 << p_stage;
- if ((p_capabilities->subgroup_in_shaders & stage_bit) == stage_bit) {
+ uint32_t subgroup_in_shaders = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
+ uint32_t subgroup_operations = uint32_t(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS));
+ if ((subgroup_in_shaders & stage_bit) == stage_bit) {
// stage supports subgroups
preamble += "#define has_GL_KHR_shader_subgroup_basic 1\n";
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_vote 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_arithmetic 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_ballot 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_shuffle 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_shuffle_relative 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_clustered 1\n";
}
- if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
+ if (subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
preamble += "#define has_GL_KHR_shader_subgroup_quad 1\n";
}
}
}
- if (p_capabilities->supports_multiview) {
+ if (p_render_device->has_feature(RD::SUPPORTS_MULTIVIEW)) {
preamble += "#define has_VK_KHR_multiview 1\n";
}
@@ -184,9 +187,10 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
return ret;
}
-static String _get_cache_key_function_glsl(const RenderingDevice::Capabilities *p_capabilities) {
+static String _get_cache_key_function_glsl(const RenderingDevice *p_render_device) {
+ const RD::Capabilities *capabilities = p_render_device->get_device_capabilities();
String version;
- version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(p_capabilities->version_major) + ", minor=" + itos(p_capabilities->version_minor) + " , subgroup_size=" + itos(p_capabilities->subgroup_operations) + " , subgroup_ops=" + itos(p_capabilities->subgroup_operations) + " , subgroup_in_shaders=" + itos(p_capabilities->subgroup_in_shaders);
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(capabilities->version_major) + ", minor=" + itos(capabilities->version_minor) + " , subgroup_size=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_SIZE)) + " , subgroup_ops=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_OPERATIONS)) + " , subgroup_in_shaders=" + itos(p_render_device->limit_get(RD::LIMIT_SUBGROUP_IN_SHADERS));
return version;
}
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 03b4380ff4..3c28546ad7 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -7,65 +7,52 @@
<tutorials>
</tutorials>
<methods>
- <method name="export_post">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
+ <method name="_export_node" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="gltf_node" type="GLTFNode" />
+ <argument index="2" name="json" type="Dictionary" />
+ <argument index="3" name="node" type="Node" />
<description>
</description>
</method>
- <method name="export_preflight">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <argument index="1" name="node" type="Node" />
+ <method name="_export_post" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
- <method name="get_export_setting" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="key" type="StringName" />
+ <method name="_export_preflight" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="root" type="Node" />
<description>
</description>
</method>
- <method name="get_export_setting_keys" qualifiers="const">
- <return type="Array" />
+ <method name="_import_node" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="gltf_node" type="GLTFNode" />
+ <argument index="2" name="json" type="Dictionary" />
+ <argument index="3" name="node" type="Node" />
<description>
</description>
</method>
- <method name="get_import_setting" qualifiers="const">
- <return type="Variant" />
- <argument index="0" name="key" type="StringName" />
+ <method name="_import_post" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
+ <argument index="1" name="root" type="Node" />
<description>
</description>
</method>
- <method name="get_import_setting_keys" qualifiers="const">
- <return type="Array" />
+ <method name="_import_post_parse" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
- <method name="import_post">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <argument index="1" name="node" type="Node" />
- <description>
- </description>
- </method>
- <method name="import_preflight">
- <return type="int" enum="Error" />
- <argument index="0" name="document" type="GLTFDocument" />
- <description>
- </description>
- </method>
- <method name="set_export_setting">
- <return type="void" />
- <argument index="0" name="key" type="StringName" />
- <argument index="1" name="value" type="Variant" />
- <description>
- </description>
- </method>
- <method name="set_import_setting">
- <return type="void" />
- <argument index="0" name="key" type="StringName" />
- <argument index="1" name="value" type="Variant" />
+ <method name="_import_preflight" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="state" type="GLTFState" />
<description>
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index a59fa900bc..44a1723563 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -192,6 +192,8 @@
</method>
</methods>
<members>
+ <member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
+ </member>
<member name="buffers" type="Array" setter="set_buffers" getter="get_buffers" default="[]">
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 173d5131cf..033591a0b9 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -58,7 +58,7 @@ void EditorSceneFormatImporterBlend::get_extensions(List<String> *r_extensions)
}
Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
@@ -239,7 +239,7 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
}
Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) {
+ const HashMap<StringName, Variant> &p_options) {
if (p_path.get_extension().to_lower() != "blend") {
return true;
}
@@ -310,7 +310,7 @@ static bool _test_blender_path(const String &p_path, String *r_err = nullptr) {
Error err = OS::get_singleton()->execute(path, args, &pipe);
if (err != OK) {
if (r_err) {
- *r_err = TTR("Can't excecute Blender binary.");
+ *r_err = TTR("Can't execute Blender binary.");
}
return false;
}
@@ -449,8 +449,8 @@ bool EditorFileSystemImportFormatSupportQueryBlend::query() {
EditorNode::get_singleton()->get_gui_base()->add_child(configure_blender_dialog);
- configure_blender_dialog->get_ok_button()->set_text(TTR("Confirm Path"));
- configure_blender_dialog->get_cancel_button()->set_text(TTR("Disable '.blend' Import"));
+ configure_blender_dialog->set_ok_button_text(TTR("Confirm Path"));
+ configure_blender_dialog->set_cancel_button_text(TTR("Disable '.blend' Import"));
configure_blender_dialog->get_cancel_button()->set_tooltip(TTR("Disables Blender '.blend' files import for this project. Can be re-enabled in Project Settings."));
configure_blender_dialog->connect("confirmed", callable_mp(this, &EditorFileSystemImportFormatSupportQueryBlend::_path_confirmed));
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
index 0925333a28..dd1c1b9889 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.h
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -66,12 +66,12 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override;
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) override;
+ const HashMap<StringName, Variant> &p_options) override;
};
class LineEdit;
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.cpp b/modules/gltf/editor/editor_scene_importer_fbx.cpp
index 893d2efcec..faad2d315d 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.cpp
+++ b/modules/gltf/editor/editor_scene_importer_fbx.cpp
@@ -30,7 +30,7 @@
#include "editor_scene_importer_fbx.h"
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
#include "../gltf_document.h"
#include "../gltf_state.h"
@@ -49,7 +49,7 @@ void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) co
}
Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink.
@@ -106,7 +106,7 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
}
Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,
- const String &p_option, const Map<StringName, Variant> &p_options) {
+ const String &p_option, const HashMap<StringName, Variant> &p_options) {
return true;
}
diff --git a/modules/gltf/editor/editor_scene_importer_fbx.h b/modules/gltf/editor/editor_scene_importer_fbx.h
index 84de7fd1cc..b0039b1c8f 100644
--- a/modules/gltf/editor/editor_scene_importer_fbx.h
+++ b/modules/gltf/editor/editor_scene_importer_fbx.h
@@ -45,12 +45,12 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override;
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,
- const Map<StringName, Variant> &p_options) override;
+ const HashMap<StringName, Variant> &p_options) override;
};
#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.cpp b/modules/gltf/editor/editor_scene_importer_gltf.cpp
index 5e7811ad2b..3fadec5167 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor/editor_scene_importer_gltf.cpp
@@ -35,7 +35,6 @@
#include "../gltf_document.h"
#include "../gltf_state.h"
-#include "scene/main/node.h"
#include "scene/resources/animation.h"
uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
@@ -48,7 +47,7 @@ void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) c
}
Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
Ref<GLTFDocument> doc;
doc.instantiate();
diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h
index b714ada124..b17a1e4eaa 100644
--- a/modules/gltf/editor/editor_scene_importer_gltf.h
+++ b/modules/gltf/editor/editor_scene_importer_gltf.h
@@ -33,6 +33,9 @@
#ifdef TOOLS_ENABLED
+#include "../gltf_document_extension.h"
+#include "../gltf_state.h"
+
#include "editor/import/resource_importer_scene.h"
class Animation;
@@ -45,7 +48,7 @@ public:
virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- const Map<StringName, Variant> &p_options, int p_bake_fps,
+ const HashMap<StringName, Variant> &p_options, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err = nullptr) override;
};
diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h
index 6bf1bf543a..f412dc2c7f 100644
--- a/modules/gltf/gltf_accessor.h
+++ b/modules/gltf/gltf_accessor.h
@@ -34,7 +34,6 @@
#include "core/io/resource.h"
#include "gltf_document.h"
-#include "gltf_document_extension.h"
struct GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
diff --git a/modules/gltf/gltf_animation.cpp b/modules/gltf/gltf_animation.cpp
index c857be4b2c..e598c870ab 100644
--- a/modules/gltf/gltf_animation.cpp
+++ b/modules/gltf/gltf_animation.cpp
@@ -45,7 +45,7 @@ void GLTFAnimation::set_loop(bool p_val) {
loop = p_val;
}
-Map<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
+HashMap<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
return tracks;
}
diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/gltf_animation.h
index ba8ae8a273..8688ddb937 100644
--- a/modules/gltf/gltf_animation.h
+++ b/modules/gltf/gltf_animation.h
@@ -64,11 +64,11 @@ public:
public:
bool get_loop() const;
void set_loop(bool p_val);
- Map<int, GLTFAnimation::Track> &get_tracks();
+ HashMap<int, GLTFAnimation::Track> &get_tracks();
GLTFAnimation();
private:
bool loop = false;
- Map<int, Track> tracks;
+ HashMap<int, Track> tracks;
};
#endif // GLTF_ANIMATION_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index 082b4ce1ec..babdc9f33b 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -128,10 +128,10 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
state->buffers.push_back(Vector<uint8_t>());
}
- /* STEP 1 CONVERT MESH INSTANCES */
+ /* STEP CONVERT MESH INSTANCES */
_convert_mesh_instances(state);
- /* STEP 2 SERIALIZE CAMERAS */
+ /* STEP SERIALIZE CAMERAS */
Error err = _serialize_cameras(state);
if (err != OK) {
return Error::FAILED;
@@ -143,37 +143,37 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
return Error::FAILED;
}
- /* STEP 5 SERIALIZE MESHES (we have enough info now) */
+ /* STEP SERIALIZE MESHES (we have enough info now) */
err = _serialize_meshes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 6 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_materials(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 7 SERIALIZE ANIMATIONS */
+ /* STEP SERIALIZE ANIMATIONS */
err = _serialize_animations(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 8 SERIALIZE ACCESSORS */
+ /* STEP SERIALIZE ACCESSORS */
err = _encode_accessors(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 9 SERIALIZE IMAGES */
+ /* STEP SERIALIZE IMAGES */
err = _serialize_images(state, p_path);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 10 SERIALIZE TEXTURES */
+ /* STEP SERIALIZE TEXTURES */
err = _serialize_textures(state);
if (err != OK) {
return Error::FAILED;
@@ -183,42 +183,49 @@ Error GLTFDocument::_serialize(Ref<GLTFState> state, const String &p_path) {
state->buffer_views.write[i]->buffer = 0;
}
- /* STEP 11 SERIALIZE BUFFER VIEWS */
+ /* STEP SERIALIZE BUFFER VIEWS */
err = _encode_buffer_views(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 12 SERIALIZE NODES */
+ /* STEP SERIALIZE NODES */
err = _serialize_nodes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 13 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_scenes(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 14 SERIALIZE SCENE */
+ /* STEP SERIALIZE SCENE */
err = _serialize_lights(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 15 SERIALIZE EXTENSIONS */
+ /* STEP SERIALIZE EXTENSIONS */
err = _serialize_extensions(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 16 SERIALIZE VERSION */
+ /* STEP SERIALIZE VERSION */
err = _serialize_version(state);
if (err != OK) {
return Error::FAILED;
}
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->export_post(state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+
return OK;
}
@@ -442,6 +449,15 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
}
node["children"] = children;
}
+
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ ERR_CONTINUE(!state->scene_nodes.find(i));
+ Error err = ext->export_node(state, n, state->json, state->scene_nodes[i]);
+ ERR_CONTINUE(err != OK);
+ }
+
nodes.push_back(node);
}
state->json["nodes"] = nodes;
@@ -2294,7 +2310,7 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true);
}
}
- Map<int, int> joint_i_to_bone_i;
+ HashMap<int, int> joint_i_to_bone_i;
for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
GLTFSkinIndex skin_i = -1;
if (state->nodes[node_i]->mesh == gltf_mesh_i) {
@@ -2468,9 +2484,9 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
mat = import_mesh->get_surface_material(surface_i);
}
if (mat.is_valid()) {
- Map<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Element *material_cache_i = state->material_cache.find(mat);
- if (material_cache_i && material_cache_i->get() != -1) {
- primitive["material"] = material_cache_i->get();
+ HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex>::Iterator material_cache_i = state->material_cache.find(mat);
+ if (material_cache_i && material_cache_i->value != -1) {
+ primitive["material"] = material_cache_i->value;
} else {
GLTFMaterialIndex mat_i = state->materials.size();
state->materials.push_back(mat);
@@ -3064,9 +3080,9 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
// We'll assume that we use either URI or bufferView, so let's warn the user
// if their image somehow uses both. And fail if it has neither.
- ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specific an 'uri' or 'bufferView'.");
+ ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specify an 'uri' or 'bufferView'.");
if (d.has("uri") && d.has("bufferView")) {
- WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'bufferView' will take precedence.");
+ WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'uri' will take precedence.");
}
String mimetype;
@@ -3186,12 +3202,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
state->images.push_back(Ref<Texture2D>());
continue;
}
-
- Ref<ImageTexture> t;
- t.instantiate();
- t->create_from_image(img);
-
- state->images.push_back(t);
+ state->images.push_back(ImageTexture::create_from_image(img));
}
print_verbose("glTF: Total images: " + itos(state->images.size()));
@@ -3412,7 +3423,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
}
orm_image->generate_mipmaps();
- orm_texture->create_from_image(orm_image);
+ orm_texture->set_image(orm_image);
GLTFTextureIndex orm_texture_index = -1;
if (has_ao || has_roughness || has_metalness) {
orm_texture->set_name(material->get_name() + "_orm");
@@ -3460,7 +3471,7 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
img->set_pixel(x, y, c);
}
}
- tex->create_from_image(img);
+ tex->set_image(img);
}
}
}
@@ -3768,13 +3779,8 @@ void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Re
}
rm_img->generate_mipmaps();
r_spec_gloss->diffuse_img->generate_mipmaps();
- Ref<ImageTexture> diffuse_image_texture;
- diffuse_image_texture.instantiate();
- diffuse_image_texture->create_from_image(r_spec_gloss->diffuse_img);
- p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_image_texture);
- Ref<ImageTexture> rm_image_texture;
- rm_image_texture.instantiate();
- rm_image_texture->create_from_image(rm_img);
+ p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, ImageTexture::create_from_image(r_spec_gloss->diffuse_img));
+ Ref<ImageTexture> rm_image_texture = ImageTexture::create_from_image(rm_img);
if (has_roughness) {
p_material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, rm_image_texture);
p_material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN);
@@ -5160,19 +5166,16 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, const GLTFNodeIndex
}
const float range = CLAMP(l->range, 0, 4096);
- // Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
- // We want to have double intensity give double brightness, so we need half the attenuation.
- const float attenuation = range / (intensity * 2048);
if (l->light_type == "point") {
OmniLight3D *light = memnew(OmniLight3D);
- light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(OmniLight3D::PARAM_ENERGY, intensity);
light->set_param(OmniLight3D::PARAM_RANGE, range);
light->set_color(l->color);
return light;
}
if (l->light_type == "spot") {
SpotLight3D *light = memnew(SpotLight3D);
- light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(SpotLight3D::PARAM_ENERGY, intensity);
light->set_param(SpotLight3D::PARAM_RANGE, range);
light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l->outer_cone_angle));
light->set_color(l->color);
@@ -5237,14 +5240,12 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig
l->light_type = "point";
OmniLight3D *light = cast_to<OmniLight3D>(p_light);
l->range = light->get_param(OmniLight3D::PARAM_RANGE);
- float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION);
- l->intensity = l->range / (attenuation * 2048);
+ l->intensity = light->get_param(OmniLight3D::PARAM_ENERGY);
} else if (cast_to<SpotLight3D>(p_light)) {
l->light_type = "spot";
SpotLight3D *light = cast_to<SpotLight3D>(p_light);
l->range = light->get_param(SpotLight3D::PARAM_RANGE);
- float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION);
- l->intensity = l->range / (attenuation * 2048);
+ l->intensity = light->get_param(SpotLight3D::PARAM_ENERGY);
l->outer_cone_angle = Math::deg2rad(light->get_param(SpotLight3D::PARAM_SPOT_ANGLE));
// This equation is the inverse of the import equation (which has a desmos link).
@@ -5658,7 +5659,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
if (!current_node) {
current_node = _generate_spatial(state, node_index);
}
- scene_parent->add_child(current_node);
+ scene_parent->add_child(current_node, true);
if (current_node != scene_root) {
current_node->set_owner(scene_root);
}
@@ -5904,9 +5905,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Node *root = ap->get_parent();
ERR_FAIL_COND(root == nullptr);
- Map<GLTFNodeIndex, Node *>::Element *node_element = state->scene_nodes.find(node_index);
- ERR_CONTINUE_MSG(node_element == nullptr, vformat("Unable to find node %d for animation", node_index));
- node_path = root->get_path_to(node_element->get());
+ HashMap<GLTFNodeIndex, Node *>::Iterator node_element = state->scene_nodes.find(node_index);
+ ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation", node_index));
+ node_path = root->get_path_to(node_element->value);
if (gltf_node->skeleton >= 0) {
const Skeleton3D *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton;
@@ -6116,11 +6117,11 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
if (node->mesh < 0) {
continue;
}
- Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(mi_node_i);
if (!mi_element) {
continue;
}
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get());
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->value);
if (!mi) {
continue;
}
@@ -6250,11 +6251,11 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
if (node->skin >= 0 && node->mesh >= 0) {
const GLTFSkinIndex skin_i = node->skin;
- Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i);
- ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i));
+ HashMap<GLTFNodeIndex, Node *>::Iterator mi_element = state->scene_nodes.find(node_i);
+ ERR_CONTINUE_MSG(!mi_element, vformat("Unable to find node %d", node_i));
- ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->get());
- ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->get()->get_class_name()));
+ ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(mi_element->value);
+ ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to ImporterMeshInstance3D", node_i, mi_element->value->get_class_name()));
const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton;
Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i];
@@ -6436,10 +6437,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &position_scene_node_i : state->scene_nodes) {
if (position_scene_node_i.value == node) {
GLTFNodeIndex node_index = position_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *position_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator position_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (position_track_i) {
- track = position_track_i->get();
+ track = position_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6452,10 +6453,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &rotation_degree_scene_node_i : state->scene_nodes) {
if (rotation_degree_scene_node_i.value == node) {
GLTFNodeIndex node_index = rotation_degree_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (rotation_degree_track_i) {
- track = rotation_degree_track_i->get();
+ track = rotation_degree_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6468,10 +6469,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &scale_scene_node_i : state->scene_nodes) {
if (scale_scene_node_i.value == node) {
GLTFNodeIndex node_index = scale_scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *scale_track_i = gltf_animation->get_tracks().find(node_index);
+ HashMap<int, GLTFAnimation::Track>::Iterator scale_track_i = gltf_animation->get_tracks().find(node_index);
GLTFAnimation::Track track;
if (scale_track_i) {
- track = scale_track_i->get();
+ track = scale_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
@@ -6503,7 +6504,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
ERR_CONTINUE(mesh_index == -1);
- Map<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
+ HashMap<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track();
if (!tracks.has(mesh_index)) {
for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
@@ -6565,10 +6566,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone];
- Map<int, GLTFAnimation::Track>::Element *property_track_i = gltf_animation->get_tracks().find(node_i);
+ HashMap<int, GLTFAnimation::Track>::Iterator property_track_i = gltf_animation->get_tracks().find(node_i);
GLTFAnimation::Track track;
if (property_track_i) {
- track = property_track_i->get();
+ track = property_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
@@ -6580,10 +6581,10 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : state->scene_nodes) {
if (scene_node_i.value == godot_node) {
GLTFNodeIndex node_i = scene_node_i.key;
- Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_i);
+ HashMap<int, GLTFAnimation::Track>::Iterator node_track_i = gltf_animation->get_tracks().find(node_i);
GLTFAnimation::Track track;
if (node_track_i) {
- track = node_track_i->get();
+ track = node_track_i->value;
}
track = _convert_animation_track(state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
@@ -6642,8 +6643,8 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess>
for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
ERR_CONTINUE(ext.is_null());
- err = ext->import_preflight(this);
- ERR_FAIL_COND_V(err != OK, FAILED);
+ err = ext->import_preflight(state);
+ ERR_FAIL_COND_V(err != OK, err);
}
err = _parse_gltf_state(state, p_path, p_bake_fps);
ERR_FAIL_COND_V(err != OK, err);
@@ -6870,12 +6871,10 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> state) {
Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_path) {
ERR_FAIL_NULL_V(state, ERR_INVALID_PARAMETER);
-
Error err = _serialize(state, p_path);
if (err != OK) {
return err;
}
-
err = _serialize_file(state, p_path);
if (err != OK) {
return Error::FAILED;
@@ -6886,6 +6885,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa
Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
+ Error err = OK;
GLTFNodeIndex gltf_root = state->root_nodes.write[0];
Node *gltf_root_node = state->get_scene_node(gltf_root);
Node *root = gltf_root_node->get_parent();
@@ -6899,12 +6899,26 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
_import_animation(state, ap, i, p_bake_fps);
}
}
-
+ for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
+ ERR_CONTINUE(!E.value);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ ERR_CONTINUE(!state->json.has("nodes"));
+ Array nodes = state->json["nodes"];
+ ERR_CONTINUE(E.key >= nodes.size());
+ ERR_CONTINUE(E.key < 0);
+ Dictionary node_json = nodes[E.key];
+ Ref<GLTFNode> gltf_node = state->nodes[E.key];
+ err = ext->import_node(state, gltf_node, node_json, E.value);
+ ERR_CONTINUE(err != OK);
+ }
+ }
for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
ERR_CONTINUE(ext.is_null());
- Error err = ext->import_post(this, root);
- ERR_FAIL_COND_V(err != OK, nullptr);
+ err = ext->import_post(state, root);
+ ERR_CONTINUE(err != OK);
}
ERR_FAIL_NULL_V(root, nullptr);
return root;
@@ -6919,13 +6933,23 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32
if (!state->buffers.size()) {
state->buffers.push_back(Vector<uint8_t>());
}
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ }
- /* STEP 1 CONVERT MESH INSTANCES */
- _convert_mesh_instances(state);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ Error err = ext->export_preflight(p_node);
+ ERR_FAIL_COND_V(err != OK, FAILED);
+ }
+ _convert_scene_node(state, p_node, -1, -1);
+ if (!state->buffers.size()) {
+ state->buffers.push_back(Vector<uint8_t>());
+ }
- /* STEP 2 CREATE SKINS */
- Error err = _serialize_skins(state);
- return err;
+ return OK;
}
Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) {
@@ -6938,8 +6962,15 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
- err = _parse(state, p_base_path.get_base_dir(), file_access, p_bake_fps);
- ERR_FAIL_COND_V(err != OK, FAILED);
+ state->base_path = p_base_path.get_base_dir();
+ err = _parse(state, state->base_path, file_access, p_bake_fps);
+ ERR_FAIL_COND_V(err != OK, err);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->import_post_parse(state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
return OK;
}
@@ -7030,6 +7061,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_sear
for (int32_t root_i = 0; root_i < state->root_nodes.size(); root_i++) {
_generate_scene_node(state, root, root, state->root_nodes[root_i]);
}
+
return OK;
}
@@ -7049,9 +7081,16 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
if (base_path.is_empty()) {
base_path = p_path.get_base_dir();
}
+ r_state->base_path = base_path;
err = _parse(r_state, base_path, f, p_bake_fps);
- ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- return err;
+ ERR_FAIL_COND_V(err != OK, err);
+ for (int32_t ext_i = 0; ext_i < document_extensions.size(); ext_i++) {
+ Ref<GLTFDocumentExtension> ext = document_extensions[ext_i];
+ ERR_CONTINUE(ext.is_null());
+ err = ext->import_post_parse(r_state);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+ return OK;
}
Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> state) {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 19bc507a8d..2f61210ff9 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -33,19 +33,12 @@
#include "gltf_animation.h"
-#include "core/error/error_list.h"
-#include "core/variant/dictionary.h"
-#include "core/variant/variant.h"
-#include "gltf_document_extension_convert_importer_mesh.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/material.h"
-#include "scene/resources/texture.h"
#include "modules/modules_enabled.gen.h" // For csg, gridmap.
@@ -135,12 +128,12 @@ private:
}
template <class T>
- static Array to_array(const Set<T> &p_inp) {
+ static Array to_array(const HashSet<T> &p_inp) {
Array ret;
- typename Set<T>::Element *elem = p_inp.front();
+ typename HashSet<T>::Iterator elem = p_inp.begin();
while (elem) {
- ret.push_back(elem->get());
- elem = elem->next();
+ ret.push_back(*elem);
+ ++elem;
}
return ret;
}
@@ -154,23 +147,23 @@ private:
}
template <class T>
- static void set_from_array(Set<T> &r_out, const Array &p_inp) {
+ static void set_from_array(HashSet<T> &r_out, const Array &p_inp) {
r_out.clear();
for (int i = 0; i < p_inp.size(); i++) {
r_out.insert(p_inp[i]);
}
}
template <class K, class V>
- static Dictionary to_dict(const Map<K, V> &p_inp) {
+ static Dictionary to_dict(const HashMap<K, V> &p_inp) {
Dictionary ret;
- for (typename Map<K, V>::Element *E = p_inp.front(); E; E = E->next()) {
- ret[E->key()] = E->value();
+ for (const KeyValue<K, V> &E : p_inp) {
+ ret[E.key] = E.value;
}
return ret;
}
template <class K, class V>
- static void set_from_dict(Map<K, V> &r_out, const Dictionary &p_inp) {
+ static void set_from_dict(HashMap<K, V> &r_out, const Dictionary &p_inp) {
r_out.clear();
Array keys = p_inp.keys();
for (int i = 0; i < keys.size(); i++) {
diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp
index 192a1d347c..d0bd7651e0 100644
--- a/modules/gltf/gltf_document_extension.cpp
+++ b/modules/gltf/gltf_document_extension.cpp
@@ -30,59 +30,79 @@
#include "gltf_document_extension.h"
-#include "gltf_document.h"
-
void GLTFDocumentExtension::_bind_methods() {
- // Import
- ClassDB::bind_method(D_METHOD("get_import_setting_keys"),
- &GLTFDocumentExtension::get_import_setting_keys);
- ClassDB::bind_method(D_METHOD("import_preflight", "document"),
- &GLTFDocumentExtension::import_preflight);
- ClassDB::bind_method(D_METHOD("get_import_setting", "key"),
- &GLTFDocumentExtension::get_import_setting);
- ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"),
- &GLTFDocumentExtension::set_import_setting);
- ClassDB::bind_method(D_METHOD("import_post", "document", "node"),
- &GLTFDocumentExtension::import_post);
- // Export
- ClassDB::bind_method(D_METHOD("get_export_setting_keys"),
- &GLTFDocumentExtension::get_export_setting_keys);
- ClassDB::bind_method(D_METHOD("get_export_setting", "key"),
- &GLTFDocumentExtension::get_export_setting);
- ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"),
- &GLTFDocumentExtension::set_export_setting);
- ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"),
- &GLTFDocumentExtension::export_preflight);
- ClassDB::bind_method(D_METHOD("export_post", "document"),
- &GLTFDocumentExtension::export_post);
+ GDVIRTUAL_BIND(_import_preflight, "state");
+ GDVIRTUAL_BIND(_import_post_parse, "state");
+ GDVIRTUAL_BIND(_import_node, "state", "gltf_node", "json", "node");
+ GDVIRTUAL_BIND(_import_post, "state", "root");
+ GDVIRTUAL_BIND(_export_preflight, "root");
+ GDVIRTUAL_BIND(_export_node, "state", "gltf_node", "json", "node");
+ GDVIRTUAL_BIND(_export_post, "state");
}
-Array GLTFDocumentExtension::get_import_setting_keys() const {
- return import_settings.keys();
+Error GLTFDocumentExtension::import_post(Ref<GLTFState> p_state, Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_post, p_state, p_root, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const {
- if (!import_settings.has(p_key)) {
- return Variant();
+Error GLTFDocumentExtension::import_preflight(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_preflight, p_state, err)) {
+ return Error(err);
}
- return import_settings[p_key];
+ return OK;
}
-void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) {
- import_settings[p_key] = p_var;
+Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_post_parse, p_state, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Array GLTFDocumentExtension::get_export_setting_keys() const {
- return import_settings.keys();
+Error GLTFDocumentExtension::export_post(Ref<GLTFState> p_state) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_post, p_state, err)) {
+ return Error(err);
+ }
+ return OK;
+}
+Error GLTFDocumentExtension::export_preflight(Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_preflight, p_root, err)) {
+ return Error(err);
+ }
+ return OK;
}
-Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const {
- if (!import_settings.has(p_key)) {
- return Variant();
+Error GLTFDocumentExtension::import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_import_node, p_state, p_gltf_node, r_dict, p_node, err)) {
+ return Error(err);
}
- return import_settings[p_key];
+ return OK;
}
-void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) {
- import_settings[p_key] = p_var;
+Error GLTFDocumentExtension::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_dict, Node *p_node) {
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_gltf_node, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+ int err = OK;
+ if (GDVIRTUAL_CALL(_export_node, p_state, p_gltf_node, r_dict, p_node, err)) {
+ return Error(err);
+ }
+ return OK;
}
diff --git a/modules/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h
index f7a3531282..556f79e887 100644
--- a/modules/gltf/gltf_document_extension.h
+++ b/modules/gltf/gltf_document_extension.h
@@ -31,33 +31,31 @@
#ifndef GLTF_DOCUMENT_EXTENSION_H
#define GLTF_DOCUMENT_EXTENSION_H
-#include "core/io/resource.h"
-#include "core/variant/dictionary.h"
-#include "core/variant/typed_array.h"
-#include "core/variant/variant.h"
-class GLTFDocument;
+#include "gltf_accessor.h"
+#include "gltf_node.h"
+#include "gltf_state.h"
+
class GLTFDocumentExtension : public Resource {
GDCLASS(GLTFDocumentExtension, Resource);
- Dictionary import_settings;
- Dictionary export_settings;
-
protected:
static void _bind_methods();
public:
- virtual Array get_import_setting_keys() const;
- virtual Variant get_import_setting(const StringName &p_key) const;
- virtual void set_import_setting(const StringName &p_key, Variant p_var);
- virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; }
- virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
-
-public:
- virtual Array get_export_setting_keys() const;
- virtual Variant get_export_setting(const StringName &p_key) const;
- virtual void set_export_setting(const StringName &p_key, Variant p_var);
- virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
- virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; }
+ virtual Error import_preflight(Ref<GLTFState> p_state);
+ virtual Error import_post_parse(Ref<GLTFState> p_state);
+ virtual Error export_post(Ref<GLTFState> p_state);
+ virtual Error import_post(Ref<GLTFState> p_state, Node *p_node);
+ virtual Error export_preflight(Node *p_state);
+ virtual Error import_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
+ virtual Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_json, Node *p_node);
+ GDVIRTUAL1R(int, _import_preflight, Ref<GLTFState>);
+ GDVIRTUAL1R(int, _import_post_parse, Ref<GLTFState>);
+ GDVIRTUAL4R(int, _import_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
+ GDVIRTUAL2R(int, _import_post, Ref<GLTFState>, Node *);
+ GDVIRTUAL1R(int, _export_preflight, Node *);
+ GDVIRTUAL4R(int, _export_node, Ref<GLTFState>, Ref<GLTFNode>, Dictionary, Node *);
+ GDVIRTUAL1R(int, _export_post, Ref<GLTFState>);
};
#endif // GLTF_DOCUMENT_EXTENSION_H
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
index 47a3e5598f..1620900a04 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
@@ -29,41 +29,40 @@
/*************************************************************************/
#include "gltf_document_extension_convert_importer_mesh.h"
+
+#include "gltf_state.h"
+
#include "core/error/error_macros.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/importer_mesh.h"
-#include <cstddef>
-
void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
}
-Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) {
- ERR_FAIL_NULL_V(p_document, ERR_INVALID_PARAMETER);
- ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFState> p_state, Node *p_root) {
+ ERR_FAIL_NULL_V(p_root, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_state, ERR_INVALID_PARAMETER);
List<Node *> queue;
- queue.push_back(p_node);
+ queue.push_back(p_root);
List<Node *> delete_queue;
while (!queue.is_empty()) {
List<Node *>::Element *E = queue.front();
Node *node = E->get();
- {
- ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node);
- if (mesh_3d) {
- MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
- Ref<ImporterMesh> mesh = mesh_3d->get_mesh();
- if (mesh.is_valid()) {
- Ref<ArrayMesh> array_mesh = mesh->get_mesh();
- mesh_instance_node_3d->set_name(node->get_name());
- mesh_instance_node_3d->set_transform(mesh_3d->get_transform());
- mesh_instance_node_3d->set_mesh(array_mesh);
- mesh_instance_node_3d->set_skin(mesh_3d->get_skin());
- mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
- node->replace_by(mesh_instance_node_3d);
- delete_queue.push_back(node);
- } else {
- memdelete(mesh_instance_node_3d);
- }
+ ImporterMeshInstance3D *mesh_3d = cast_to<ImporterMeshInstance3D>(node);
+ if (mesh_3d) {
+ MeshInstance3D *mesh_instance_node_3d = memnew(MeshInstance3D);
+ Ref<ImporterMesh> mesh = mesh_3d->get_mesh();
+ if (mesh.is_valid()) {
+ Ref<ArrayMesh> array_mesh = mesh->get_mesh();
+ mesh_instance_node_3d->set_name(node->get_name());
+ mesh_instance_node_3d->set_transform(mesh_3d->get_transform());
+ mesh_instance_node_3d->set_mesh(array_mesh);
+ mesh_instance_node_3d->set_skin(mesh_3d->get_skin());
+ mesh_instance_node_3d->set_skeleton_path(mesh_3d->get_skeleton_path());
+ node->replace_by(mesh_instance_node_3d);
+ delete_queue.push_back(node);
+ } else {
+ memdelete(mesh_instance_node_3d);
}
}
int child_count = node->get_child_count();
diff --git a/modules/gltf/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
index 2d51143140..4c9e42a00f 100644
--- a/modules/gltf/gltf_document_extension_convert_importer_mesh.h
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
@@ -31,14 +31,10 @@
#ifndef GLTF_EXTENSION_EDITOR_H
#define GLTF_EXTENSION_EDITOR_H
-#include "core/io/resource.h"
-#include "core/variant/dictionary.h"
-
-#include "gltf_document.h"
#include "gltf_document_extension.h"
+
#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
-#include "scene/main/node.h"
#include "scene/resources/importer_mesh.h"
class GLTFDocumentExtension;
@@ -50,6 +46,6 @@ protected:
static void _bind_methods();
public:
- Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override;
+ Error import_post(Ref<GLTFState> p_state, Node *p_root) override;
};
#endif // GLTF_EXTENSION_EDITOR_H
diff --git a/modules/gltf/gltf_skeleton.cpp b/modules/gltf/gltf_skeleton.cpp
index e80376f130..b813f39a27 100644
--- a/modules/gltf/gltf_skeleton.cpp
+++ b/modules/gltf/gltf_skeleton.cpp
@@ -46,7 +46,7 @@ void GLTFSkeleton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // Map<int32_t,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // RBMap<int32_t,
}
Vector<GLTFNodeIndex> GLTFSkeleton::get_joints() {
diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/gltf_skeleton.h
index 7d07d528cb..92ee6e6234 100644
--- a/modules/gltf/gltf_skeleton.h
+++ b/modules/gltf/gltf_skeleton.h
@@ -50,9 +50,9 @@ private:
Skeleton3D *godot_skeleton = nullptr;
// Set of unique bone names for the skeleton
- Set<String> unique_names;
+ HashSet<String> unique_names;
- Map<int32_t, GLTFNodeIndex> godot_bone_node;
+ HashMap<int32_t, GLTFNodeIndex> godot_bone_node;
Vector<BoneAttachment3D *> bone_attachments;
@@ -78,10 +78,10 @@ public:
Array get_unique_names();
void set_unique_names(Array p_unique_names);
- //Map<int32_t, GLTFNodeIndex> get_godot_bone_node() {
+ //RBMap<int32_t, GLTFNodeIndex> get_godot_bone_node() {
// return this->godot_bone_node;
//}
- //void set_godot_bone_node(Map<int32_t, GLTFNodeIndex> p_godot_bone_node) {
+ //void set_godot_bone_node(RBMap<int32_t, GLTFNodeIndex> p_godot_bone_node) {
// this->godot_bone_node = p_godot_bone_node;
//}
Dictionary get_godot_bone_node();
diff --git a/modules/gltf/gltf_skin.cpp b/modules/gltf/gltf_skin.cpp
index 283fc34ff5..e8005aa0c1 100644
--- a/modules/gltf/gltf_skin.cpp
+++ b/modules/gltf/gltf_skin.cpp
@@ -59,8 +59,8 @@ void GLTFSkin::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // int
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // Map<int,
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // Map<int,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // RBMap<int,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // RBMap<int,
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "godot_skin"), "set_godot_skin", "get_godot_skin"); // Ref<Skin>
}
@@ -130,16 +130,16 @@ void GLTFSkin::set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i) {
Dictionary GLTFSkin::get_joint_i_to_name() {
Dictionary ret;
- Map<int, StringName>::Element *elem = joint_i_to_name.front();
+ HashMap<int, StringName>::Iterator elem = joint_i_to_name.begin();
while (elem) {
- ret[elem->key()] = String(elem->value());
- elem = elem->next();
+ ret[elem->key] = String(elem->value);
+ ++elem;
}
return ret;
}
void GLTFSkin::set_joint_i_to_name(Dictionary p_joint_i_to_name) {
- joint_i_to_name = Map<int, StringName>();
+ joint_i_to_name = HashMap<int, StringName>();
Array keys = p_joint_i_to_name.keys();
for (int i = 0; i < keys.size(); i++) {
joint_i_to_name[keys[i]] = p_joint_i_to_name[keys[i]];
diff --git a/modules/gltf/gltf_skin.h b/modules/gltf/gltf_skin.h
index 31cb892f19..d946324756 100644
--- a/modules/gltf/gltf_skin.h
+++ b/modules/gltf/gltf_skin.h
@@ -65,8 +65,8 @@ private:
// A mapping from the joint indices (in the order of joints_original) to the
// Godot Skeleton's bone_indices
- Map<int, int> joint_i_to_bone_i;
- Map<int, StringName> joint_i_to_name;
+ HashMap<int, int> joint_i_to_bone_i;
+ HashMap<int, StringName> joint_i_to_name;
// The Actual Skin that will be created as a mapping between the IBM's of
// this skin to the generated skeleton for the mesh instances.
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index 6ead2f69c3..989fa476c2 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -57,6 +57,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_materials", "materials"), &GLTFState::set_materials);
ClassDB::bind_method(D_METHOD("get_scene_name"), &GLTFState::get_scene_name);
ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name);
+ ClassDB::bind_method(D_METHOD("get_base_path"), &GLTFState::get_base_path);
+ ClassDB::bind_method(D_METHOD("set_base_path", "base_path"), &GLTFState::set_base_path);
ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes);
ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
@@ -93,6 +95,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector<Ref<GLTFMesh>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material>
ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_path"), "set_base_path", "get_base_path"); // String
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture>
@@ -102,7 +105,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set<String>
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>>
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // Map<GLTFSkeletonIndex,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // RBMap<GLTFSkeletonIndex,
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
}
@@ -313,3 +316,11 @@ void GLTFState::set_discard_meshes_and_materials(bool p_discard_meshes_and_mater
bool GLTFState::get_discard_meshes_and_materials() {
return discard_meshes_and_materials;
}
+
+String GLTFState::get_base_path() {
+ return base_path;
+}
+
+void GLTFState::set_base_path(String p_base_path) {
+ base_path = p_base_path;
+}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index 42ca079f1c..2fdef19038 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -44,10 +44,7 @@
#include "gltf_skin.h"
#include "gltf_texture.h"
-#include "core/io/resource.h"
-#include "core/templates/map.h"
-#include "core/templates/pair.h"
-#include "core/templates/vector.h"
+#include "core/templates/rb_map.h"
#include "scene/animation/animation_player.h"
#include "scene/resources/texture.h"
@@ -56,6 +53,7 @@ class GLTFState : public Resource {
friend class GLTFDocument;
String filename;
+ String base_path;
Dictionary json;
int major_version = 0;
int minor_version = 0;
@@ -72,7 +70,7 @@ class GLTFState : public Resource {
Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to.
Vector<AnimationPlayer *> animation_players;
- Map<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
+ HashMap<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
Vector<Ref<BaseMaterial3D>> materials;
String scene_name;
@@ -83,16 +81,16 @@ class GLTFState : public Resource {
Vector<Ref<GLTFSkin>> skins;
Vector<Ref<GLTFCamera>> cameras;
Vector<Ref<GLTFLight>> lights;
- Set<String> unique_names;
- Set<String> unique_animation_names;
+ HashSet<String> unique_names;
+ HashSet<String> unique_animation_names;
Vector<Ref<GLTFSkeleton>> skeletons;
- Map<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node;
+ HashMap<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node;
Vector<Ref<GLTFAnimation>> animations;
- Map<GLTFNodeIndex, Node *> scene_nodes;
+ HashMap<GLTFNodeIndex, Node *> scene_nodes;
- Map<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
- Map<ObjectID, Map<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+ HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
+ HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
protected:
static void _bind_methods();
@@ -137,6 +135,9 @@ public:
String get_scene_name();
void set_scene_name(String p_scene_name);
+ String get_base_path();
+ void set_base_path(String p_base_path);
+
Array get_root_nodes();
void set_root_nodes(Array p_root_nodes);
@@ -176,7 +177,7 @@ public:
AnimationPlayer *get_animation_player(int idx);
- //void set_scene_nodes(Map<GLTFNodeIndex, Node *> p_scene_nodes) {
+ //void set_scene_nodes(RBMap<GLTFNodeIndex, Node *> p_scene_nodes) {
// this->scene_nodes = p_scene_nodes;
//}
@@ -184,10 +185,10 @@ public:
// this->animation_players = p_animation_players;
//}
- //Map<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
+ //RBMap<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
// return this->material_cache;
//}
- //void set_material_cache(Map<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
+ //void set_material_cache(RBMap<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
// this->material_cache = p_material_cache;
//}
};
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 407ce961c8..499f54e3ba 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -73,6 +73,13 @@
Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
</description>
</method>
+ <method name="get_navigation_layer_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
+ <description>
+ Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [code]layer_number[/code] between 1 and 32.
+ </description>
+ </method>
<method name="get_used_cells" qualifiers="const">
<return type="Array" />
<description>
@@ -133,6 +140,14 @@
Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_layer_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
+ <description>
+ Based on [code]value[/code], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [code]layer_number[/code] between 1 and 32.
+ </description>
+ </method>
<method name="world_to_map" qualifiers="const">
<return type="Vector3i" />
<argument index="0" name="world_position" type="Vector3" />
@@ -177,7 +192,7 @@
The assigned [MeshLibrary].
</member>
<member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
- The navigation layers the GridMap generates its navigable regions in.
+ A bitmask determining all navigation layers the GridMap generated navigation regions belong to. These navigation layers can be checked upon when requesting a path with [method NavigationServer3D.map_get_path].
</member>
<member name="physics_material" type="PhysicsMaterial" setter="set_physics_material" getter="get_physics_material">
Overrides the default friction and bounce physics properties for the whole [GridMap].
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index 2f613768ee..f975d95079 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -930,6 +930,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) {
}
update_palette();
+ _update_cursor_instance();
set_process(true);
@@ -1220,7 +1221,7 @@ GridMapEditor::GridMapEditor() {
search_box = memnew(LineEdit);
search_box->set_h_size_flags(SIZE_EXPAND_FILL);
- search_box->set_placeholder(TTR("Filter meshes"));
+ search_box->set_placeholder(TTR("Filter Meshes"));
hb->add_child(search_box);
search_box->connect("text_changed", callable_mp(this, &GridMapEditor::_text_changed));
search_box->connect("gui_input", callable_mp(this, &GridMapEditor::_sbox_input));
@@ -1259,9 +1260,9 @@ GridMapEditor::GridMapEditor() {
info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes."));
info_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
info_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- info_message->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
+ info_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
mesh_library_palette->add_child(info_message);
edit_axis = Vector3::AXIS_Y;
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 3c7bd5eb70..7d80cbef7c 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -103,9 +103,10 @@ bool GridMap::_get(const StringName &p_name, Variant &r_ret) const {
{
int *w = cells.ptrw();
int i = 0;
- for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next(), i++) {
- encode_uint64(E->key().key, (uint8_t *)&w[i * 3]);
- encode_uint32(E->get().cell, (uint8_t *)&w[i * 3 + 2]);
+ for (const KeyValue<IndexKey, Cell> &E : cell_map) {
+ encode_uint64(E.key.key, (uint8_t *)&w[i * 3]);
+ encode_uint32(E.value.cell, (uint8_t *)&w[i * 3 + 2]);
+ i++;
}
}
@@ -225,15 +226,33 @@ bool GridMap::is_baking_navigation() {
return bake_navigation;
}
-void GridMap::set_navigation_layers(uint32_t p_layers) {
- navigation_layers = p_layers;
+void GridMap::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
_recreate_octant_data();
}
-uint32_t GridMap::get_navigation_layers() {
+uint32_t GridMap::get_navigation_layers() const {
return navigation_layers;
}
+void GridMap::set_navigation_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Navigation layer number must be between 1 and 32 inclusive.");
+ uint32_t _navigation_layers = get_navigation_layers();
+ if (p_value) {
+ _navigation_layers |= 1 << (p_layer_number - 1);
+ } else {
+ _navigation_layers &= ~(1 << (p_layer_number - 1));
+ }
+ set_navigation_layers(_navigation_layers);
+}
+
+bool GridMap::get_navigation_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Navigation layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Navigation layer number must be between 1 and 32 inclusive.");
+ return get_navigation_layers() & (1 << (p_layer_number - 1));
+}
+
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
if (!mesh_library.is_null()) {
mesh_library->unregister_owner(this);
@@ -432,6 +451,18 @@ void GridMap::_octant_transform(const OctantKey &p_key) {
RS::get_singleton()->instance_set_transform(g.collision_debug_instance, get_global_transform());
}
+ // update transform for NavigationServer regions and navigation debugmesh instances
+ for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ if (bake_navigation) {
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->region_set_transform(E.value.region, get_global_transform() * E.value.xform);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_transform(E.value.navmesh_debug_instance, get_global_transform() * E.value.xform);
+ }
+ }
+ }
+
for (int i = 0; i < g.multimesh_instances.size(); i++) {
RS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
}
@@ -455,6 +486,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
//erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -480,17 +514,17 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
* and set said multimesh bounding box to one containing all cells which have this item
*/
- Map<int, List<Pair<Transform3D, IndexKey>>> multimesh_items;
+ HashMap<int, List<Pair<Transform3D, IndexKey>>> multimesh_items;
- for (Set<IndexKey>::Element *E = g.cells.front(); E; E = E->next()) {
- ERR_CONTINUE(!cell_map.has(E->get()));
- const Cell &c = cell_map[E->get()];
+ for (const IndexKey &E : g.cells) {
+ ERR_CONTINUE(!cell_map.has(E));
+ const Cell &c = cell_map[E];
if (!mesh_library.is_valid() || !mesh_library->has_item(c.item)) {
continue;
}
- Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z);
+ Vector3 cellpos = Vector3(E.x, E.y, E.z);
Vector3 ofs = _get_offset();
Transform3D xform;
@@ -506,7 +540,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Pair<Transform3D, IndexKey> p;
p.first = xform * mesh_library->get_item_mesh_transform(c.item);
- p.second = E->get();
+ p.second = E;
multimesh_items[c.item].push_back(p);
}
}
@@ -532,14 +566,29 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
if (bake_navigation) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh);
- NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * mesh_library->get_item_navmesh_transform(c.item));
+ NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * nm.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
nm.region = region;
+
+ // add navigation debugmesh visual instances if debug is enabled
+ SceneTree *st = SceneTree::get_singleton();
+ if (st && st->is_debugging_navigation_hint()) {
+ if (!nm.navmesh_debug_instance.is_valid()) {
+ RID navmesh_debug_rid = navmesh->get_debug_mesh()->get_rid();
+ nm.navmesh_debug_instance = RS::get_singleton()->instance_create();
+ RS::get_singleton()->instance_set_base(nm.navmesh_debug_instance, navmesh_debug_rid);
+ RS::get_singleton()->mesh_surface_set_material(navmesh_debug_rid, 0, st->get_debug_navigation_material()->get_rid());
+ }
+ if (is_inside_tree()) {
+ RS::get_singleton()->instance_set_scenario(nm.navmesh_debug_instance, get_world_3d()->get_scenario());
+ RS::get_singleton()->instance_set_transform(nm.navmesh_debug_instance, get_global_transform() * nm.xform);
+ }
+ }
}
- g.navmesh_ids[E->get()] = nm;
+ g.navmesh_ids[E] = nm;
}
}
@@ -628,7 +677,7 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F.key].item);
if (nm.is_valid()) {
RID region = NavigationServer3D::get_singleton()->region_create();
- NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
+ NavigationServer3D::get_singleton()->region_set_navigation_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, nm);
NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F.value.xform);
NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
@@ -659,6 +708,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
NavigationServer3D::get_singleton()->free(F.value.region);
F.value.region = RID();
}
+ if (F.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(F.value.navmesh_debug_instance);
+ F.value.navmesh_debug_instance = RID();
+ }
}
}
@@ -677,7 +730,12 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
// Erase navigation
for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
- NavigationServer3D::get_singleton()->free(E.value.region);
+ if (E.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
+ }
+ if (E.value.navmesh_debug_instance.is_valid()) {
+ RS::get_singleton()->free(E.value.navmesh_debug_instance);
+ }
}
g.navmesh_ids.clear();
@@ -770,7 +828,7 @@ void GridMap::_queue_octants_dirty() {
void GridMap::_recreate_octant_data() {
recreating_octants = true;
- Map<IndexKey, Cell> cell_copy = cell_map;
+ HashMap<IndexKey, Cell, IndexKey> cell_copy = cell_map;
_clear_internal();
for (const KeyValue<IndexKey, Cell> &E : cell_copy) {
set_cell_item(Vector3i(E.key), E.value.item, E.value.rot);
@@ -845,6 +903,9 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_navigation_layers", "layers"), &GridMap::set_navigation_layers);
ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers);
+ ClassDB::bind_method(D_METHOD("set_navigation_layer_value", "layer_number", "value"), &GridMap::set_navigation_layer_value);
+ ClassDB::bind_method(D_METHOD("get_navigation_layer_value", "layer_number"), &GridMap::get_navigation_layer_value);
+
ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library);
ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library);
@@ -889,7 +950,7 @@ void GridMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, "MeshLibrary"), "set_mesh_library", "get_mesh_library");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material", "get_physics_material");
ADD_GROUP("Cell", "cell_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cell_size", PROPERTY_HINT_NONE, "suffix:m"), "set_cell_size", "get_cell_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_octant_size", PROPERTY_HINT_RANGE, "1,1024,1"), "set_octant_size", "get_octant_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_x"), "set_center_x", "get_center_x");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cell_center_y"), "set_center_y", "get_center_y");
@@ -969,7 +1030,7 @@ Array GridMap::get_meshes() const {
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
- meshes.push_back(xform);
+ meshes.push_back(xform * mesh_library->get_item_mesh_transform(id));
meshes.push_back(mesh);
}
@@ -998,7 +1059,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
}
//generate
- Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>> surface_map;
+ HashMap<OctantKey, HashMap<Ref<Material>, Ref<SurfaceTool>>, OctantKey> surface_map;
for (KeyValue<IndexKey, Cell> &E : cell_map) {
IndexKey key = E.key;
@@ -1028,10 +1089,10 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
ok.z = key.z / octant_size;
if (!surface_map.has(ok)) {
- surface_map[ok] = Map<Ref<Material>, Ref<SurfaceTool>>();
+ surface_map[ok] = HashMap<Ref<Material>, Ref<SurfaceTool>>();
}
- Map<Ref<Material>, Ref<SurfaceTool>> &mat_map = surface_map[ok];
+ HashMap<Ref<Material>, Ref<SurfaceTool>> &mat_map = surface_map[ok];
for (int i = 0; i < mesh->get_surface_count(); i++) {
if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
@@ -1051,7 +1112,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
}
}
- for (KeyValue<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>> &E : surface_map) {
+ for (KeyValue<OctantKey, HashMap<Ref<Material>, Ref<SurfaceTool>>> &E : surface_map) {
Ref<ArrayMesh> mesh;
mesh.instantiate();
for (KeyValue<Ref<Material>, Ref<SurfaceTool>> &F : E.value) {
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 5e367e149d..078a1d9de5 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -56,9 +56,15 @@ class GridMap : public Node3D {
};
uint64_t key = 0;
+ static uint32_t hash(const IndexKey &p_key) {
+ return hash_one_uint64(p_key.key);
+ }
_FORCE_INLINE_ bool operator<(const IndexKey &p_key) const {
return key < p_key.key;
}
+ _FORCE_INLINE_ bool operator==(const IndexKey &p_key) const {
+ return key == p_key.key;
+ }
_FORCE_INLINE_ operator Vector3i() const {
return Vector3i(x, y, z);
@@ -92,6 +98,7 @@ class GridMap : public Node3D {
struct NavMesh {
RID region;
Transform3D xform;
+ RID navmesh_debug_instance;
};
struct MultimeshInstance {
@@ -107,13 +114,13 @@ class GridMap : public Node3D {
};
Vector<MultimeshInstance> multimesh_instances;
- Set<IndexKey> cells;
+ HashSet<IndexKey> cells;
RID collision_debug;
RID collision_debug_instance;
bool dirty = false;
RID static_body;
- Map<IndexKey, NavMesh> navmesh_ids;
+ HashMap<IndexKey, NavMesh> navmesh_ids;
};
union OctantKey {
@@ -126,8 +133,11 @@ class GridMap : public Node3D {
uint64_t key = 0;
- _FORCE_INLINE_ bool operator<(const OctantKey &p_key) const {
- return key < p_key.key;
+ static uint32_t hash(const OctantKey &p_key) {
+ return hash_one_uint64(p_key.key);
+ }
+ _FORCE_INLINE_ bool operator==(const OctantKey &p_key) const {
+ return key == p_key.key;
}
//OctantKey(const IndexKey& p_k, int p_item) { indexkey=p_k.key; item=p_item; }
@@ -154,8 +164,8 @@ class GridMap : public Node3D {
Ref<MeshLibrary> mesh_library;
- Map<OctantKey, Octant *> octant_map;
- Map<IndexKey, Cell> cell_map;
+ HashMap<OctantKey, Octant *, OctantKey> octant_map;
+ HashMap<IndexKey, Cell, IndexKey> cell_map;
void _recreate_octant_data();
@@ -228,8 +238,11 @@ public:
void set_bake_navigation(bool p_bake_navigation);
bool is_baking_navigation();
- void set_navigation_layers(uint32_t p_layers);
- uint32_t get_navigation_layers();
+ void set_navigation_layers(uint32_t p_navigation_layers);
+ uint32_t get_navigation_layers() const;
+
+ void set_navigation_layer_value(int p_layer_number, bool p_value);
+ bool get_navigation_layer_value(int p_layer_number) const;
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
Ref<MeshLibrary> get_mesh_library() const;
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 7c6ceeea29..b840542c1b 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -13,6 +13,7 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/jpeg-compressor/"
thirdparty_sources = [
"jpgd.cpp",
+ "jpge.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 51358876a4..0e03fa65c8 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -33,7 +33,8 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include <jpgd.h>
+#include "thirdparty/jpeg-compressor/jpgd.h"
+#include "thirdparty/jpeg-compressor/jpge.h"
#include <string.h>
Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
@@ -131,6 +132,59 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
return img;
}
+static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float p_quality) {
+ return OK;
+}
+
+class ImageLoaderJPGOSFile : public jpge::output_stream {
+public:
+ Ref<FileAccess> f;
+
+ virtual bool put_buf(const void *Pbuf, int len) {
+ f->store_buffer((const uint8_t *)Pbuf, len);
+ return true;
+ }
+};
+
+class ImageLoaderJPGOSBuffer : public jpge::output_stream {
+public:
+ Vector<uint8_t> *buffer = nullptr;
+ virtual bool put_buf(const void *Pbuf, int len) {
+ uint32_t base = buffer->size();
+ buffer->resize(base + len);
+ memcpy(buffer->ptrw() + base, Pbuf, len);
+ return true;
+ }
+};
+
+static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
+ ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), Vector<uint8_t>());
+ Ref<Image> image = p_img;
+ if (image->get_format() != Image::FORMAT_RGB8) {
+ image->convert(Image::FORMAT_ETC2_RGB8);
+ }
+
+ jpge::params p;
+ p.m_quality = CLAMP(p_quality * 100, 1, 100);
+ Vector<uint8_t> output;
+ ImageLoaderJPGOSBuffer ob;
+ ob.buffer = &output;
+
+ jpge::jpeg_encoder enc;
+ enc.init(&ob, image->get_width(), image->get_height(), 3, p);
+
+ const uint8_t *src_data = image->get_data().ptr();
+ for (int i = 0; i < image->get_height(); i++) {
+ enc.process_scanline(&src_data[i * image->get_width() * 3]);
+ }
+
+ enc.process_scanline(nullptr);
+
+ return output;
+}
+
ImageLoaderJPG::ImageLoaderJPG() {
Image::_jpg_mem_loader_func = _jpegd_mem_loader_func;
+ Image::save_jpg_func = _jpgd_save_func;
+ Image::save_jpg_buffer_func = _jpgd_buffer_save_func;
}
diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h
index 3144746f6d..f57d6aef42 100644
--- a/modules/jsonrpc/jsonrpc.h
+++ b/modules/jsonrpc/jsonrpc.h
@@ -37,7 +37,7 @@
class JSONRPC : public Object {
GDCLASS(JSONRPC, Object)
- Map<String, Object *> method_scopes;
+ HashMap<String, Object *> method_scopes;
protected:
static void _bind_methods();
diff --git a/modules/lightmapper_rd/SCsub b/modules/lightmapper_rd/SCsub
index 5cc9d8ee8b..fe9737b36f 100644
--- a/modules/lightmapper_rd/SCsub
+++ b/modules/lightmapper_rd/SCsub
@@ -7,9 +7,7 @@ env_lightmapper_rd = env_modules.Clone()
env_lightmapper_rd.GLSL_HEADER("lm_raster.glsl")
env_lightmapper_rd.GLSL_HEADER("lm_compute.glsl")
env_lightmapper_rd.GLSL_HEADER("lm_blendseams.glsl")
-env_lightmapper_rd.Depends("lm_raster.glsl.gen.h", "lm_common_inc.glsl")
-env_lightmapper_rd.Depends("lm_compute.glsl.gen.h", "lm_common_inc.glsl")
-env_lightmapper_rd.Depends("lm_blendseams.glsl.gen.h", "lm_common_inc.glsl")
+env_lightmapper_rd.Depends(Glob("*.glsl.gen.h"), ["lm_common_inc.glsl", "#glsl_builders.py"])
# Godot source files
env_lightmapper_rd.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp
index 214c60091c..83ac478a97 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -51,7 +51,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi);
}
-void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) {
+void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x;
@@ -62,11 +62,12 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct
l.color[2] = p_color.b;
l.energy = p_energy;
l.static_bake = p_static;
- l.size = p_angular_distance;
+ l.size = Math::tan(Math::deg2rad(p_angular_distance));
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
-void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) {
+void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x;
@@ -80,10 +81,11 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy;
l.static_bake = p_static;
l.size = p_size;
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
-void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) {
+void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) {
Light l;
l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x;
@@ -102,6 +104,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.energy = p_energy;
l.static_bake = p_static;
l.size = p_size;
+ l.shadow_blur = p_shadow_blur;
lights.push_back(l);
}
@@ -268,8 +271,8 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
mi.offset.x = best_atlas_offsets[m_i].x;
mi.offset.y = best_atlas_offsets[m_i].y;
mi.slice = best_atlas_offsets[m_i].z;
- albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2(Vector2(), Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height())), mi.offset);
- emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2(), Size2i(mi.data.emission_on_uv2->get_width(), mi.data.emission_on_uv2->get_height())), mi.offset);
+ albedo_images.write[mi.slice]->blit_rect(mi.data.albedo_on_uv2, Rect2i(Vector2i(), mi.data.albedo_on_uv2->get_size()), mi.offset);
+ emission_images.write[mi.slice]->blit_rect(mi.data.emission_on_uv2, Rect2(Vector2i(), mi.data.emission_on_uv2->get_size()), mi.offset);
}
return BAKE_OK;
@@ -1140,6 +1143,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1);
+ switch (p_quality) {
+ case BAKE_QUALITY_LOW: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_MEDIUM: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_HIGH: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
+ } break;
+ case BAKE_QUALITY_ULTRA: {
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
+ } break;
+ }
+
+ push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
+
RD::ComputeListID compute_list = rd->compute_list_begin();
rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_primary_pipeline);
rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
@@ -1230,23 +1250,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
uniforms.write[1].set_id(0, light_dest_tex);
secondary_uniform_set[1] = rd->uniform_set_create(uniforms, compute_shader_secondary, 1);
- switch (p_quality) {
- case BAKE_QUALITY_LOW: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
- } break;
- case BAKE_QUALITY_MEDIUM: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
- } break;
- case BAKE_QUALITY_HIGH: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
- } break;
- case BAKE_QUALITY_ULTRA: {
- push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
- } break;
- }
-
- push_constant.ray_count = CLAMP(push_constant.ray_count, 16u, 8192u);
-
int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
@@ -1417,7 +1420,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
img2.instantiate();
img2->create(2, 2, false, Image::FORMAT_RGBAF, s);
img2->convert(Image::FORMAT_RGB8);
- img->blit_rect(img2, Rect2(0, 0, 2, 2), Point2((j % 3) * 2, (j / 3) * 2));
+ img->blit_rect(img2, Rect2i(0, 0, 2, 2), Point2i((j % 3) * 2, (j / 3) * 2));
}
img->save_png("res://3_light_probe_" + itos(i) + ".png");
}
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 503f5f7009..8cb4b58a18 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -57,8 +57,9 @@ class LightmapperRD : public Lightmapper {
float attenuation = 0.0;
float cos_spot_angle = 0.0;
float inv_spot_attenuation = 0.0;
+ float shadow_blur = 0.0;
uint32_t static_bake = 0;
- uint32_t pad[3] = {};
+ uint32_t pad[2] = {};
bool operator<(const Light &p_light) const {
return type < p_light.type;
@@ -110,12 +111,12 @@ class LightmapperRD : public Lightmapper {
struct EdgeHash {
_FORCE_INLINE_ static uint32_t hash(const Edge &p_edge) {
- uint32_t h = hash_djb2_one_float(p_edge.a.x);
- h = hash_djb2_one_float(p_edge.a.y, h);
- h = hash_djb2_one_float(p_edge.a.z, h);
- h = hash_djb2_one_float(p_edge.b.x, h);
- h = hash_djb2_one_float(p_edge.b.y, h);
- h = hash_djb2_one_float(p_edge.b.z, h);
+ uint32_t h = hash_murmur3_one_float(p_edge.a.x);
+ h = hash_murmur3_one_float(p_edge.a.y, h);
+ h = hash_murmur3_one_float(p_edge.a.z, h);
+ h = hash_murmur3_one_float(p_edge.b.x, h);
+ h = hash_murmur3_one_float(p_edge.b.y, h);
+ h = hash_murmur3_one_float(p_edge.b.z, h);
return h;
}
};
@@ -146,15 +147,15 @@ class LightmapperRD : public Lightmapper {
struct VertexHash {
_FORCE_INLINE_ static uint32_t hash(const Vertex &p_vtx) {
- uint32_t h = hash_djb2_one_float(p_vtx.position[0]);
- h = hash_djb2_one_float(p_vtx.position[1], h);
- h = hash_djb2_one_float(p_vtx.position[2], h);
- h = hash_djb2_one_float(p_vtx.uv[0], h);
- h = hash_djb2_one_float(p_vtx.uv[1], h);
- h = hash_djb2_one_float(p_vtx.normal_xy[0], h);
- h = hash_djb2_one_float(p_vtx.normal_xy[1], h);
- h = hash_djb2_one_float(p_vtx.normal_z, h);
- return h;
+ uint32_t h = hash_murmur3_one_float(p_vtx.position[0]);
+ h = hash_murmur3_one_float(p_vtx.position[1], h);
+ h = hash_murmur3_one_float(p_vtx.position[2], h);
+ h = hash_murmur3_one_float(p_vtx.uv[0], h);
+ h = hash_murmur3_one_float(p_vtx.uv[1], h);
+ h = hash_murmur3_one_float(p_vtx.normal_xy[0], h);
+ h = hash_murmur3_one_float(p_vtx.normal_xy[1], h);
+ h = hash_murmur3_one_float(p_vtx.normal_z, h);
+ return hash_fmix32(h);
}
};
@@ -236,9 +237,9 @@ class LightmapperRD : public Lightmapper {
public:
virtual void add_mesh(const MeshData &p_mesh) override;
- virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance) override;
- virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size) override;
- virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size) override;
+ virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override;
+ virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override;
+ virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override;
virtual void add_probe(const Vector3 &p_position) override;
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, GenerateProbes p_generate_probes, const Ref<Image> &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr) override;
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 58523dc1f8..e0e8000f56 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -51,8 +51,9 @@ struct Light {
float cos_spot_angle;
float inv_spot_attenuation;
+ float shadow_blur;
bool static_bake;
- uint pad[3];
+ uint pad[2];
};
layout(set = 0, binding = 4, std430) restrict readonly buffer Lights {
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 0b6b72a310..efa6cd50b4 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -316,19 +316,24 @@ void main() {
for (uint i = 0; i < params.light_count; i++) {
vec3 light_pos;
+ float dist;
float attenuation;
+ float soft_shadowing_disk_size;
if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL) {
vec3 light_vec = lights.data[i].direction;
light_pos = position - light_vec * length(params.world_size);
+ dist = length(params.world_size);
attenuation = 1.0;
+ soft_shadowing_disk_size = lights.data[i].size;
} else {
light_pos = lights.data[i].position;
- float d = distance(position, light_pos);
- if (d > lights.data[i].range) {
+ dist = distance(position, light_pos);
+ if (dist > lights.data[i].range) {
continue;
}
+ soft_shadowing_disk_size = lights.data[i].size / dist;
- attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation);
+ attenuation = get_omni_attenuation(dist, 1.0 / lights.data[i].range, lights.data[i].attenuation);
if (lights.data[i].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(position - light_pos);
@@ -352,27 +357,70 @@ void main() {
continue; //no need to do anything
}
- if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) {
- vec3 light = lights.data[i].color * lights.data[i].energy * attenuation;
- if (lights.data[i].static_bake) {
- static_light += light;
-#ifdef USE_SH_LIGHTMAPS
+ float penumbra = 0.0;
+ if (lights.data[i].size > 0.0) {
+ vec3 light_to_point = -light_dir;
+ vec3 aux = light_to_point.y < 0.777 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
+ vec3 light_to_point_tan = normalize(cross(light_to_point, aux));
+ vec3 light_to_point_bitan = normalize(cross(light_to_point, light_to_point_tan));
+
+ const uint shadowing_rays_check_penumbra_denom = 2;
+ uint shadowing_ray_count = params.ray_count;
+
+ uint hits = 0;
+ uint noise = random_seed(ivec3(atlas_pos, 43573547 /* some prime */));
+ vec3 light_disk_to_point = light_to_point;
+ for (uint j = 0; j < shadowing_ray_count; j++) {
+ // Optimization:
+ // Once already traced an important proportion of rays, if all are hits or misses,
+ // assume we're not in the penumbra so we can infer the rest would have the same result
+ if (j == shadowing_ray_count / shadowing_rays_check_penumbra_denom) {
+ if (hits == j) {
+ // Assume totally lit
+ hits = shadowing_ray_count;
+ break;
+ } else if (hits == 0) {
+ // Assume totally dark
+ hits = 0;
+ break;
+ }
+ }
- float c[4] = float[](
- 0.282095, //l0
- 0.488603 * light_dir.y, //l1n1
- 0.488603 * light_dir.z, //l1n0
- 0.488603 * light_dir.x //l1p1
- );
+ float r = randomize(noise);
+ float a = randomize(noise) * 2.0 * PI;
+ vec2 disk_sample = (r * vec2(cos(a), sin(a))) * soft_shadowing_disk_size * lights.data[i].shadow_blur;
+ light_disk_to_point = normalize(light_to_point + disk_sample.x * light_to_point_tan + disk_sample.y * light_to_point_bitan);
- for (uint j = 0; j < 4; j++) {
- sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
+ if (trace_ray(position - light_disk_to_point * params.bias, position - light_disk_to_point * dist) == RAY_MISS) {
+ hits++;
}
-#endif
+ }
+ penumbra = float(hits) / float(shadowing_ray_count);
+ } else {
+ if (trace_ray(position + light_dir * params.bias, light_pos) == RAY_MISS) {
+ penumbra = 1.0;
+ }
+ }
- } else {
- dynamic_light += light;
+ vec3 light = lights.data[i].color * lights.data[i].energy * attenuation * penumbra;
+ if (lights.data[i].static_bake) {
+ static_light += light;
+#ifdef USE_SH_LIGHTMAPS
+
+ float c[4] = float[](
+ 0.282095, //l0
+ 0.488603 * light_dir.y, //l1n1
+ 0.488603 * light_dir.z, //l1n0
+ 0.488603 * light_dir.x //l1p1
+ );
+
+ for (uint j = 0; j < 4; j++) {
+ sh_accum[j].rgb += light * c[j] * (1.0 / 3.0);
}
+#endif
+
+ } else {
+ dynamic_light += light;
}
}
diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp
index 1818048877..92590fbcf6 100644
--- a/modules/mbedtls/stream_peer_mbedtls.cpp
+++ b/modules/mbedtls/stream_peer_mbedtls.cpp
@@ -298,6 +298,10 @@ StreamPeerMbedTLS::Status StreamPeerMbedTLS::get_status() const {
return status;
}
+Ref<StreamPeer> StreamPeerMbedTLS::get_stream() const {
+ return base;
+}
+
StreamPeerSSL *StreamPeerMbedTLS::_create_func() {
return memnew(StreamPeerMbedTLS);
}
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index 98b91e65ab..7660410e04 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -57,6 +57,7 @@ public:
virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>());
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>());
virtual Status get_status() const;
+ virtual Ref<StreamPeer> get_stream() const;
virtual void disconnect_from_stream();
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index b5b51403f7..c37bea519f 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -38,7 +38,9 @@
#include "core/io/file_access.h"
int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- ERR_FAIL_COND_V(!active, 0);
+ if (!active) {
+ return 0;
+ }
int todo = p_frames;
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index 6cd710e792..e03940f963 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -54,7 +54,7 @@ String ResourceImporterMP3::get_resource_type() const {
return "AudioStreamMP3";
}
-bool ResourceImporterMP3::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const {
+bool ResourceImporterMP3::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
@@ -71,7 +71,7 @@ void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOp
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
}
-Error ResourceImporterMP3::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, Variant *r_metadata) {
+Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
bool loop = p_options["loop"];
float loop_offset = p_options["loop_offset"];
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 75890228b9..678a3773bb 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -48,9 +48,9 @@ public:
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
- 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;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
ResourceImporterMP3();
};
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index 1920ef1c1a..3459244bc2 100644
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -43,7 +43,7 @@ String get_godot_android_mono_config() {
Compression::decompress(w, config_uncompressed_size, config_compressed_data,
config_compressed_size, Compression::MODE_DEFLATE);
String s;
- if (s.parse_utf8((const char *)w, data.size())) {
+ if (s.parse_utf8((const char *)w, data.size()) != OK) {
ERR_FAIL_V(String());
}
return s;
diff --git a/modules/mono/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index 3afde1e8d3..c4547b4323 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -124,7 +124,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
List<StringName> snames;
- for (const KeyValue<StringName, int> &F : t->constant_map) {
+ for (const KeyValue<StringName, int64_t> &F : t->constant_map) {
snames.push_back(F.key);
}
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 31257ac33a..3dc26cfbe4 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -686,10 +686,10 @@ void CSharpLanguage::pre_unsafe_unreference(Object *p_obj) {
#ifdef DEBUG_ENABLED
MutexLock lock(unsafe_object_references_lock);
ObjectID id = p_obj->get_instance_id();
- Map<ObjectID, int>::Element *elem = unsafe_object_references.find(id);
+ HashMap<ObjectID, int>::Iterator elem = unsafe_object_references.find(id);
ERR_FAIL_NULL(elem);
- if (--elem->value() == 0) {
- unsafe_object_references.erase(elem);
+ if (--elem->value == 0) {
+ unsafe_object_references.remove(elem);
}
#endif
}
@@ -875,7 +875,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
- for (Object *&obj : script->instances) {
+ for (Object *obj : script->instances) {
script->pending_reload_instances.insert(obj->get_instance_id());
RefCounted *rc = Object::cast_to<RefCounted>(obj);
@@ -885,7 +885,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#ifdef TOOLS_ENABLED
- for (PlaceHolderScriptInstance *&script_instance : script->placeholders) {
+ for (PlaceHolderScriptInstance *script_instance : script->placeholders) {
Object *obj = script_instance->get_owner();
script->pending_reload_instances.insert(obj->get_instance_id());
@@ -897,9 +897,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Save state and remove script from instances
- Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
+ RBMap<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
- for (Object *&obj : script->instances) {
+ for (Object *obj : script->instances) {
ERR_CONTINUE(!obj->get_script_instance());
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
@@ -922,8 +922,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// After the state of all instances is saved, clear scripts and script instances
for (Ref<CSharpScript> &script : scripts) {
- while (script->instances.front()) {
- Object *obj = script->instances.front()->get();
+ while (script->instances.begin()) {
+ Object *obj = *script->instances.begin();
obj->set_script(Ref<RefCounted>()); // Remove script and existing script instances (placeholder are not removed before domain reload)
}
@@ -1099,14 +1099,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
const StringName &name = G.first;
const Array &serialized_data = G.second;
- Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
+ HashMap<StringName, CSharpScript::EventSignal>::Iterator match = script->event_signals.find(name);
if (!match) {
// The event or its signal attribute were removed
continue;
}
- const CSharpScript::EventSignal &event_signal = match->value();
+ const CSharpScript::EventSignal &event_signal = match->value;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
MonoDelegate *delegate = nullptr;
@@ -1428,7 +1428,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
return true;
}
-Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
+RBMap<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
return script_bindings.insert(p_object, p_script_binding);
}
@@ -1437,7 +1437,7 @@ void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance
MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
+ RBMap<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
if (match) {
return (void *)match;
}
@@ -1467,7 +1467,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
{
MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding;
+ RBMap<Object *, CSharpScriptBinding>::Element *data = (RBMap<Object *, CSharpScriptBinding>::Element *)p_binding;
CSharpScriptBinding &script_binding = data->value();
@@ -1488,7 +1488,7 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
CRASH_COND(!p_binding);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner);
@@ -1558,7 +1558,7 @@ void *CSharpLanguage::get_instance_binding(Object *p_object) {
// `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that.
if (binding) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)binding)->value();
if (!script_binding.inited) {
MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
@@ -2301,7 +2301,7 @@ CSharpInstance::~CSharpInstance() {
void *data = CSharpLanguage::get_instance_binding(owner);
CRASH_COND(data == nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
CRASH_COND(!script_binding.inited);
#ifdef DEBUG_ENABLED
@@ -2315,9 +2315,9 @@ CSharpInstance::~CSharpInstance() {
#ifdef DEBUG_ENABLED
// CSharpInstance must not be created unless it's going to be added to the list for sure
- Set<Object *>::Element *match = script->instances.find(owner);
+ HashSet<Object *>::Iterator match = script->instances.find(owner);
CRASH_COND(!match);
- script->instances.erase(match);
+ script->instances.remove(match);
#else
script->instances.erase(owner);
#endif
@@ -2331,7 +2331,7 @@ void CSharpScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#endif
#ifdef TOOLS_ENABLED
-void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) {
+void CSharpScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) {
if (base_cache.is_valid()) {
base_cache->_update_exports_values(values, propnames);
}
@@ -2567,12 +2567,12 @@ bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_upda
if ((changed || p_instance_to_update) && placeholders.size()) {
// Update placeholders if any
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
if (changed) {
- for (PlaceHolderScriptInstance *&script_instance : placeholders) {
+ for (PlaceHolderScriptInstance *script_instance : placeholders) {
script_instance->update(propnames, values);
}
} else {
@@ -2807,7 +2807,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
GD_MONO_ASSERT_THREAD_ATTACHED;
if (p_variant_type == Variant::INT && p_type.type_encoding == MONO_TYPE_VALUETYPE && mono_class_is_enum(p_type.type_class->get_mono_ptr())) {
- r_hint = PROPERTY_HINT_ENUM;
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
+ r_hint = GDMonoUtils::Marshal::type_has_flags_attribute(reftype) ? PROPERTY_HINT_FLAGS : PROPERTY_HINT_ENUM;
Vector<MonoClassField *> fields = p_type.type_class->get_enum_fields();
@@ -2844,7 +2845,8 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
uint64_t val = GDMonoUtils::unbox_enum_value(val_obj, enum_basetype, r_error);
ERR_FAIL_COND_V_MSG(r_error, -1, "Failed to unbox '" + enum_field_name + "' constant enum value.");
- if (val != (unsigned int)i) {
+ unsigned int expected_val = r_hint == PROPERTY_HINT_FLAGS ? 1 << i : i;
+ if (val != expected_val) {
uses_default_values = false;
}
@@ -2864,6 +2866,12 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
r_hint = PROPERTY_HINT_RESOURCE_TYPE;
r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
+ } else if (p_variant_type == Variant::OBJECT && CACHED_CLASS(Node)->is_assignable_from(p_type.type_class)) {
+ GDMonoClass *field_native_class = GDMonoUtils::get_class_native_base(p_type.type_class);
+ CRASH_COND(field_native_class == nullptr);
+
+ r_hint = PROPERTY_HINT_NODE_TYPE;
+ r_hint_string = String(NATIVE_GDMONOCLASS_NAME(field_native_class));
} else if (p_allow_generics && p_variant_type == Variant::ARRAY) {
// Nested arrays are not supported in the inspector
@@ -3061,16 +3069,11 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
Vector<GDMonoMethod *> methods = top->get_all_methods();
for (int i = 0; i < methods.size(); i++) {
if (!methods[i]->is_static()) {
- Multiplayer::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
- if (Multiplayer::RPC_MODE_DISABLED != mode) {
- Multiplayer::RPCConfig nd;
- nd.name = methods[i]->get_name();
- nd.rpc_mode = mode;
- // TODO Transfer mode, channel
- nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
- nd.channel = 0;
- if (-1 == p_script->rpc_functions.find(nd)) {
- p_script->rpc_functions.push_back(nd);
+ Multiplayer::RPCConfig rpc_config = p_script->_member_get_rpc_config(methods[i]);
+ if (rpc_config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
+ // RPC annotations can only be used once per method
+ if (p_script->rpc_functions.find(rpc_config) == -1) {
+ p_script->rpc_functions.push_back(rpc_config);
}
}
}
@@ -3144,7 +3147,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
void *data = CSharpLanguage::get_existing_instance_binding(p_owner);
CRASH_COND(data == nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited && !script_binding.gchandle.is_released()) {
MonoObject *mono_object = script_binding.gchandle.get_target();
if (mono_object) {
@@ -3401,9 +3404,9 @@ ScriptLanguage *CSharpScript::get_language() const {
bool CSharpScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
#ifdef TOOLS_ENABLED
- const Map<StringName, Variant>::Element *E = exported_members_defval_cache.find(p_property);
+ HashMap<StringName, Variant>::ConstIterator E = exported_members_defval_cache.find(p_property);
if (E) {
- r_value = E->get();
+ r_value = E->value;
return true;
}
@@ -3505,15 +3508,19 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
-Multiplayer::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
- if (p_member->has_attribute(CACHED_CLASS(AnyPeerAttribute))) {
- return Multiplayer::RPC_MODE_ANY_PEER;
- }
- if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) {
- return Multiplayer::RPC_MODE_AUTHORITY;
+Multiplayer::RPCConfig CSharpScript::_member_get_rpc_config(IMonoClassMember *p_member) const {
+ Multiplayer::RPCConfig rpc_config;
+
+ MonoObject *rpc_attribute = p_member->get_attribute(CACHED_CLASS(RPCAttribute));
+ if (rpc_attribute != nullptr) {
+ rpc_config.name = p_member->get_name();
+ rpc_config.rpc_mode = (Multiplayer::RPCMode)CACHED_PROPERTY(RPCAttribute, Mode)->get_int_value(rpc_attribute);
+ rpc_config.call_local = CACHED_PROPERTY(RPCAttribute, CallLocal)->get_bool_value(rpc_attribute);
+ rpc_config.transfer_mode = (Multiplayer::TransferMode)CACHED_PROPERTY(RPCAttribute, TransferMode)->get_int_value(rpc_attribute);
+ rpc_config.channel = CACHED_PROPERTY(RPCAttribute, TransferChannel)->get_int_value(rpc_attribute);
}
- return Multiplayer::RPC_MODE_DISABLED;
+ return rpc_config;
}
const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const {
@@ -3574,7 +3581,7 @@ CSharpScript::~CSharpScript() {
#endif
}
-void CSharpScript::get_members(Set<StringName> *p_members) {
+void CSharpScript::get_members(HashSet<StringName> *p_members) {
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
if (p_members) {
for (const StringName &member_name : exported_members_names) {
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index c69cba61a4..b17473470f 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -110,7 +110,7 @@ private:
Ref<CSharpScript> base_cache; // TODO what's this for?
- Set<Object *> instances;
+ HashSet<Object *> instances;
#ifdef GD_MONO_HOT_RELOAD
struct StateBackup {
@@ -121,8 +121,8 @@ private:
List<Pair<StringName, Array>> event_signals;
};
- Set<ObjectID> pending_reload_instances;
- Map<ObjectID, StateBackup> pending_reload_state;
+ HashSet<ObjectID> pending_reload_instances;
+ RBMap<ObjectID, StateBackup> pending_reload_state;
StringName tied_class_name_for_reload;
StringName tied_class_namespace_for_reload;
#endif
@@ -132,26 +132,26 @@ private:
SelfList<CSharpScript> script_list = this;
- Map<StringName, Vector<SignalParameter>> _signals;
- Map<StringName, EventSignal> event_signals;
+ HashMap<StringName, Vector<SignalParameter>> _signals;
+ HashMap<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
- Map<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
- Set<PlaceHolderScriptInstance *> placeholders;
+ HashMap<StringName, Variant> exported_members_defval_cache; // member_default_values_cache
+ HashSet<PlaceHolderScriptInstance *> placeholders;
bool source_changed_cache = false;
bool placeholder_fallback_enabled = false;
bool exports_invalidated = true;
- void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ void _update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames);
void _update_member_info_no_exports();
void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
- Set<StringName> exported_members_names;
+ HashSet<StringName> exported_members_names;
#endif
HashMap<StringName, PropertyInfo> member_info;
@@ -179,7 +179,7 @@ private:
static void update_script_class_info(Ref<CSharpScript> p_script);
static void initialize_for_managed_type(Ref<CSharpScript> p_script, GDMonoClass *p_class, GDMonoClass *p_native);
- Multiplayer::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
+ Multiplayer::RPCConfig _member_get_rpc_config(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
@@ -218,7 +218,7 @@ public:
void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
- void get_members(Set<StringName> *p_members) override;
+ void get_members(HashSet<StringName> *p_members) override;
bool is_tool() const override { return tool; }
bool is_valid() const override { return valid; }
@@ -356,11 +356,11 @@ class CSharpLanguage : public ScriptLanguage {
Mutex script_gchandle_release_mutex;
Mutex language_bind_mutex;
- Map<Object *, CSharpScriptBinding> script_bindings;
+ RBMap<Object *, CSharpScriptBinding> script_bindings;
#ifdef DEBUG_ENABLED
// List of unsafe object references
- Map<ObjectID, int> unsafe_object_references;
+ HashMap<ObjectID, int> unsafe_object_references;
Mutex unsafe_object_references_lock;
#endif
@@ -467,7 +467,7 @@ public:
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override;
/* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions,
- List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
+ List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override {
return true;
}
String validate_path(const String &p_path) const override;
@@ -502,6 +502,7 @@ public:
/* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {}
/* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {}
+ /* TODO? */ void get_public_annotations(List<MethodInfo> *p_annotations) const override {}
void reload_all_scripts() override;
void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;
@@ -518,7 +519,7 @@ public:
void thread_enter() override;
void thread_exit() override;
- Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
+ RBMap<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
#ifdef DEBUG_ENABLED
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
index ae78da27bc..303ca3a293 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/GodotTools.IdeMessaging.CLI.csproj
@@ -12,6 +12,6 @@
<ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />
</ItemGroup>
<ItemGroup>
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
index dad6b9ae7a..02f1764f32 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj
@@ -19,6 +19,6 @@ A client using this library is only compatible with servers of the same major ve
</Description>
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 9e8f7ef1b1..3c020a2589 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -126,7 +126,7 @@ namespace GodotTools.Build
{
base._Ready();
- MinimumSize = new Vector2(0, 228) * EditorScale;
+ CustomMinimumSize = new Vector2(0, 228) * EditorScale;
SizeFlagsVertical = (int)SizeFlags.ExpandFill;
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
index 16dd1c8c6b..63b97e981e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -99,7 +99,7 @@ namespace GodotTools.Build
if (Utils.OS.IsWindows)
{
// %APPDATA% for both
- return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
+ return new[] { Path.Combine(applicationData, "NuGet", "NuGet.Config") };
}
var paths = new string[2];
@@ -156,6 +156,7 @@ namespace GodotTools.Build
</packageSources>
</configuration>
";
+ System.IO.Directory.CreateDirectory(Path.GetDirectoryName(nuGetConfigPath));
System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index b9aa760f4d..f1d45463c5 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -19,7 +19,7 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
- <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
+ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 168f0254ee..7cc195201b 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -604,14 +604,14 @@ void BindingsGenerator::_append_xml_signal(StringBuilder &p_xml_output, const Ty
void BindingsGenerator::_append_xml_enum(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts) {
const StringName search_cname = !p_target_itype ? p_target_cname : StringName(p_target_itype->name + "." + (String)p_target_cname);
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname);
+ HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(search_cname);
if (!enum_match && search_cname != p_target_cname) {
enum_match = enum_types.find(p_target_cname);
}
if (enum_match) {
- const TypeInterface &target_enum_itype = enum_match->value();
+ const TypeInterface &target_enum_itype = enum_match->value;
p_xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
p_xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
@@ -954,7 +954,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
}
}
- p_output.append(MEMBER_BEGIN "public const int ");
+ p_output.append(MEMBER_BEGIN "public const long ");
p_output.append(iconstant.proxy_name);
p_output.append(" = ");
p_output.append(itos(iconstant.value));
@@ -990,8 +990,13 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n" INDENT1 OPEN_BLOCK);
}
+ if (ienum.is_flags) {
+ p_output.append("\n" INDENT1 "[System.Flags]");
+ }
+
p_output.append("\n" INDENT1 "public enum ");
p_output.append(enum_proxy_name);
+ p_output.append(" : long");
p_output.append("\n" INDENT1 OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
@@ -1417,7 +1422,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
}
- output.append(MEMBER_BEGIN "public const int ");
+ output.append(MEMBER_BEGIN "public const long ");
output.append(iconstant.proxy_name);
output.append(" = ");
output.append(itos(iconstant.value));
@@ -1433,8 +1438,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
for (const EnumInterface &ienum : itype.enums) {
ERR_FAIL_COND_V(ienum.constants.is_empty(), ERR_BUG);
+ if (ienum.is_flags) {
+ output.append(MEMBER_BEGIN "[System.Flags]");
+ }
+
output.append(MEMBER_BEGIN "public enum ");
output.append(ienum.cname.operator String());
+ output.append(" : long");
output.append(MEMBER_BEGIN OPEN_BLOCK);
const ConstantInterface &last = ienum.constants.back()->get();
@@ -1652,7 +1662,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append("static ");
}
- p_output.append(prop_itype->cs_type);
+ String prop_cs_type = prop_itype->cs_type + _get_generic_type_parameters(*prop_itype, proptype_name.generic_type_parameters);
+
+ p_output.append(prop_cs_type);
p_output.append(" ");
p_output.append(p_iprop.proxy_name);
p_output.append("\n" INDENT2 OPEN_BLOCK);
@@ -1762,6 +1774,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
}
+ String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
+
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
@@ -1773,7 +1787,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
arguments_sig += "Nullable<";
}
- arguments_sig += arg_type->cs_type;
+ arguments_sig += arg_cs_type;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
arguments_sig += "> ";
@@ -1800,7 +1814,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String arg_in = iarg.name;
arg_in += "_in";
- cs_in_statements += arg_type->cs_type;
+ cs_in_statements += arg_cs_type;
cs_in_statements += " ";
cs_in_statements += arg_in;
cs_in_statements += " = ";
@@ -1820,7 +1834,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements += " : ";
}
- String cs_type = arg_type->cs_type;
+ String cs_type = arg_cs_type;
if (cs_type.ends_with("[]")) {
cs_type = cs_type.substr(0, cs_type.length() - 2);
}
@@ -1837,7 +1851,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Escape < and > in the attribute default value
String param_def_arg = def_arg.replacen("<", "&lt;").replacen(">", "&gt;");
- default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + param_def_arg + "</param>");
+ default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is <c>" + param_def_arg + "</c>.</param>");
} else {
icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
@@ -1903,7 +1917,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
- p_output.append(return_type->cs_type + " ");
+ String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);
+
+ p_output.append(return_cs_type + " ");
p_output.append(p_imethod.proxy_name + "(");
p_output.append(arguments_sig + ")\n" OPEN_BLOCK_L2);
@@ -1914,7 +1930,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("return;\n" CLOSE_BLOCK_L2);
} else {
p_output.append("return default(");
- p_output.append(return_type->cs_type);
+ p_output.append(return_cs_type);
p_output.append(");\n" CLOSE_BLOCK_L2);
}
@@ -1938,10 +1954,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
return OK; // Won't increment method bind count
}
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
ERR_FAIL_NULL_V(match, ERR_BUG);
- const InternalCall *im_icall = match->value();
+ const InternalCall *im_icall = match->value;
String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
im_call += ".";
@@ -1956,7 +1972,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else if (return_type->cs_out.is_empty()) {
p_output.append("return " + im_call + "(" + icall_params + ");\n");
} else {
- p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
+ p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out));
p_output.append("\n");
}
@@ -2322,10 +2338,10 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
i++;
}
- const Map<const MethodInterface *, const InternalCall *>::Element *match = method_icalls_map.find(&p_imethod);
+ HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
ERR_FAIL_NULL_V(match, ERR_BUG);
- const InternalCall *im_icall = match->value();
+ const InternalCall *im_icall = match->value;
String icall_method = im_icall->name;
if (!generated_icall_funcs.find(im_icall)) {
@@ -2468,29 +2484,29 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
- const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator builtin_type_match = builtin_types.find(p_typeref.cname);
if (builtin_type_match) {
- return &builtin_type_match->get();
+ return &builtin_type_match->value;
}
- const HashMap<StringName, TypeInterface>::Iterator obj_type_match = obj_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator obj_type_match = obj_types.find(p_typeref.cname);
if (obj_type_match) {
return &obj_type_match->value;
}
if (p_typeref.is_enum) {
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator enum_match = enum_types.find(p_typeref.cname);
if (enum_match) {
- return &enum_match->get();
+ return &enum_match->value;
}
// Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
- const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
+ HashMap<StringName, TypeInterface>::ConstIterator int_match = builtin_types.find(name_cache.type_int);
ERR_FAIL_NULL_V(int_match, nullptr);
- return &int_match->get();
+ return &int_match->value;
}
return nullptr;
@@ -2505,16 +2521,52 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placehol
ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
- const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
+ HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname);
if (match) {
- return &match->get();
+ return &match->value;
}
TypeInterface placeholder;
TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
- return &placeholder_types.insert(placeholder.cname, placeholder)->get();
+ return &placeholder_types.insert(placeholder.cname, placeholder)->value;
+}
+
+const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
+ if (p_generic_type_parameters.is_empty()) {
+ return "";
+ }
+
+ ERR_FAIL_COND_V_MSG(p_itype.type_parameter_count != p_generic_type_parameters.size(), "",
+ "Generic type parameter count mismatch for type '" + p_itype.name + "'." +
+ " Found " + itos(p_generic_type_parameters.size()) + ", but requires " +
+ itos(p_itype.type_parameter_count) + ".");
+
+ int i = 0;
+ String params = "<";
+ for (const TypeReference &param_type : p_generic_type_parameters) {
+ const TypeInterface *param_itype = _get_type_or_placeholder(param_type);
+
+ ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
+ "Generic type parameter is a singleton: '" + param_itype->name + "'.");
+
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(param_itype->api_type == ClassDB::API_EDITOR, "",
+ "Generic type parameter '" + param_itype->name + "' has type from the editor API." +
+ " Core API cannot have dependencies on the editor API.");
+ }
+
+ params += param_itype->cs_type;
+ if (i < p_generic_type_parameters.size() - 1) {
+ params += ", ";
+ }
+
+ i++;
+ }
+ params += ">";
+
+ return params;
}
StringName BindingsGenerator::_get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta) {
@@ -2708,7 +2760,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
List<PropertyInfo> property_list;
ClassDB::get_property_list(type_cname, &property_list, true);
- Map<StringName, StringName> accessor_methods;
+ HashMap<StringName, StringName> accessor_methods;
for (const PropertyInfo &property : property_list) {
if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) {
@@ -2821,7 +2873,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
" We only expected Object.free, but found '" +
itype.name + "." + imethod.name + "'.");
}
- } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ } else if (return_info.type == Variant::INT && return_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
imethod.return_type.cname = return_info.class_name;
imethod.return_type.is_enum = true;
} else if (return_info.class_name != StringName()) {
@@ -2832,6 +2884,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ERR_FAIL_COND_V_MSG(bad_reference_hint, false,
String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
+ } else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -2856,11 +2911,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = orig_arg_name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
iarg.type.cname = arginfo.class_name;
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -2903,9 +2961,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
imethod.proxy_name += "_";
}
- Map<StringName, StringName>::Element *accessor = accessor_methods.find(imethod.cname);
+ HashMap<StringName, StringName>::Iterator accessor = accessor_methods.find(imethod.cname);
if (accessor) {
- const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value());
+ const PropertyInterface *accessor_property = itype.find_property_by_name(accessor->value);
// We only deprecate an accessor method if it's in the same class as the property. It's easier this way, but also
// we don't know if an accessor method in a different class could have other purposes, so better leave those untouched.
@@ -2961,11 +3019,14 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = orig_arg_name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ if (arginfo.type == Variant::INT && arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
iarg.type.cname = arginfo.class_name;
iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
+ } else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@@ -3022,9 +3083,9 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
List<String> constants;
ClassDB::get_integer_constant_list(type_cname, &constants, true);
- const HashMap<StringName, List<StringName>> &enum_map = class_info->enum_map;
+ const HashMap<StringName, ClassDB::ClassInfo::EnumInfo> &enum_map = class_info->enum_map;
- for (const KeyValue<StringName, List<StringName>> &E : enum_map) {
+ for (const KeyValue<StringName, ClassDB::ClassInfo::EnumInfo> &E : enum_map) {
StringName enum_proxy_cname = E.key;
String enum_proxy_name = enum_proxy_cname.operator String();
if (itype.find_property_by_proxy_name(enum_proxy_cname)) {
@@ -3034,10 +3095,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_proxy_cname = StringName(enum_proxy_name);
}
EnumInterface ienum(enum_proxy_cname);
- const List<StringName> &enum_constants = E.value;
+ ienum.is_flags = E.value.is_bitfield;
+ const List<StringName> &enum_constants = E.value.constants;
for (const StringName &constant_cname : enum_constants) {
String constant_name = constant_cname.operator String();
- int *value = class_info->constant_map.getptr(constant_cname);
+ int64_t *value = class_info->constant_map.getptr(constant_cname);
ERR_FAIL_NULL_V(value, false);
constants.erase(constant_name);
@@ -3072,7 +3134,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
for (const String &constant_name : constants) {
- int *value = class_info->constant_map.getptr(StringName(constant_name));
+ int64_t *value = class_info->constant_map.getptr(StringName(constant_name));
ERR_FAIL_NULL_V(value, false);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
@@ -3549,13 +3611,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Array";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 1;
itype.c_out = "\treturn memnew(Array(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
@@ -3565,13 +3628,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Dictionary";
itype.cname = itype.name;
itype.proxy_name = itype.name;
+ itype.type_parameter_count = 2;
itype.c_out = "\treturn memnew(Dictionary(%1));\n";
itype.c_type = itype.name;
itype.c_type_in = itype.c_type + "*";
itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new " + itype.cs_type + "(%0(%1));";
+ itype.cs_out = "return new %2(%0(%1));";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
builtin_types.insert(itype.cname, itype);
@@ -3594,11 +3658,11 @@ void BindingsGenerator::_populate_global_constants() {
int global_constants_count = CoreConstants::get_global_constant_count();
if (global_constants_count > 0) {
- Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
+ HashMap<String, DocData::ClassDoc>::Iterator match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
CRASH_COND_MSG(!match, "Could not find '@GlobalScope' in DocData.");
- const DocData::ClassDoc &global_scope_doc = match->value();
+ const DocData::ClassDoc &global_scope_doc = match->value;
for (int i = 0; i < global_constants_count; i++) {
String constant_name = CoreConstants::get_global_constant_name(i);
@@ -3613,7 +3677,7 @@ void BindingsGenerator::_populate_global_constants() {
}
}
- int constant_value = CoreConstants::get_global_constant_value(i);
+ int64_t constant_value = CoreConstants::get_global_constant_value(i);
StringName enum_name = CoreConstants::get_global_constant_enum(i);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
@@ -3621,6 +3685,7 @@ void BindingsGenerator::_populate_global_constants() {
if (enum_name != StringName()) {
EnumInterface ienum(enum_name);
+ // TODO: ienum.is_flags is always false for core constants since they don't seem to support bitfield enums
List<EnumInterface>::Element *enum_match = global_enums.find(ienum);
if (enum_match) {
enum_match->get().constants.push_back(iconstant);
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index ec57a34c2b..1547d0ed2f 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -45,12 +45,12 @@ class BindingsGenerator {
struct ConstantInterface {
String name;
String proxy_name;
- int value = 0;
+ int64_t value = 0;
const DocData::ConstantDoc *const_doc;
ConstantInterface() {}
- ConstantInterface(const String &p_name, const String &p_proxy_name, int p_value) {
+ ConstantInterface(const String &p_name, const String &p_proxy_name, int64_t p_value) {
name = p_name;
proxy_name = p_proxy_name;
value = p_value;
@@ -60,6 +60,7 @@ class BindingsGenerator {
struct EnumInterface {
StringName cname;
List<ConstantInterface> constants;
+ bool is_flags = false;
_FORCE_INLINE_ bool operator==(const EnumInterface &p_ienum) const {
return p_ienum.cname == cname;
@@ -87,6 +88,8 @@ class BindingsGenerator {
StringName cname;
bool is_enum = false;
+ List<TypeReference> generic_type_parameters;
+
TypeReference() {}
TypeReference(const StringName &p_cname) :
@@ -206,6 +209,8 @@ class BindingsGenerator {
String name;
StringName cname;
+ int type_parameter_count;
+
/**
* Identifier name of the base class.
*/
@@ -535,22 +540,22 @@ class BindingsGenerator {
HashMap<StringName, TypeInterface> obj_types;
- Map<StringName, TypeInterface> placeholder_types;
- Map<StringName, TypeInterface> builtin_types;
- Map<StringName, TypeInterface> enum_types;
+ HashMap<StringName, TypeInterface> placeholder_types;
+ HashMap<StringName, TypeInterface> builtin_types;
+ HashMap<StringName, TypeInterface> enum_types;
List<EnumInterface> global_enums;
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
- Map<const MethodInterface *, const InternalCall *> method_icalls_map;
+ HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
List<const InternalCall *> generated_icall_funcs;
List<InternalCall> core_custom_icalls;
List<InternalCall> editor_custom_icalls;
- Map<StringName, List<StringName>> blacklisted_methods;
+ HashMap<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -679,6 +684,8 @@ class BindingsGenerator {
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
+ const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
+
StringName _get_int_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
StringName _get_float_type_name_from_meta(GodotTypeInfo::Metadata p_meta);
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
new file mode 100644
index 0000000000..9e1b7ef580
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/basic_import_script.cs
@@ -0,0 +1,16 @@
+// meta-description: Basic import script template
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Godot.Object _PostImport(Node scene)
+ {
+ // Modify the contents of the scene upon import.
+ return scene; // Return the modified root node when you're done.
+ }
+}
+#endif
diff --git a/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
new file mode 100644
index 0000000000..bf2c9434e4
--- /dev/null
+++ b/modules/mono/editor/script_templates/EditorScenePostImport/no_comments.cs
@@ -0,0 +1,15 @@
+// meta-description: Basic import script template (no comments)
+
+#if TOOLS
+using _BINDINGS_NAMESPACE_;
+using System;
+
+[Tool]
+public partial class _CLASS_ : _BASE_
+{
+ public override Godot.Object _PostImport(Node scene)
+ {
+ return scene;
+ }
+}
+#endif
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
index ef135da51a..2febf37f05 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -2,17 +2,27 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that determines if an assembly has scripts. If so, what types of scripts the assembly has.
+ /// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyHasScriptsAttribute : Attribute
{
private readonly bool requiresLookup;
private readonly System.Type[] scriptTypes;
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
public AssemblyHasScriptsAttribute()
{
requiresLookup = true;
}
+ /// <summary>
+ /// Constructs a new AssemblyHasScriptsAttribute instance.
+ /// </summary>
+ /// <param name="scriptTypes">The specified type(s) of scripts.</param>
public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
{
requiresLookup = false;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
index e93bc89811..0b00878e8c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that disables Godot Generators.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class DisableGodotGeneratorsAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
index 6adf044886..46eb128d37 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ExportAttribute.cs
@@ -2,12 +2,20 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute used to export objects.
+ /// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ExportAttribute : Attribute
{
private PropertyHint hint;
private string hintString;
+ /// <summary>
+ /// Constructs a new ExportAttribute Instance.
+ /// </summary>
+ /// <param name="hint">A hint to the exported object.</param>
+ /// <param name="hintString">A string representing the exported object.</param>
public ExportAttribute(PropertyHint hint = PropertyHint.None, string hintString = "")
{
this.hint = hint;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
index 55848769d5..8d4ff0fdb7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/GodotMethodAttribute.cs
@@ -2,6 +2,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute for a method.
+ /// </summary>
[AttributeUsage(AttributeTargets.Method)]
internal class GodotMethodAttribute : Attribute
{
@@ -9,6 +12,10 @@ namespace Godot
public string MethodName { get { return methodName; } }
+ /// <summary>
+ /// Constructs a new GodotMethodAttribute instance.
+ /// </summary>
+ /// <param name="methodName">The name of the method.</param>
public GodotMethodAttribute(string methodName)
{
this.methodName = methodName;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
new file mode 100644
index 0000000000..0a1c8322d7
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Attribute that changes the RPC mode for the annotated <c>method</c> to the given <see cref="Mode"/>,
+ /// optionally specifying the <see cref="TransferMode"/> and <see cref="TransferChannel"/> (on supported peers).
+ /// See <see cref="RPCMode"/> and <see cref="TransferMode"/>. By default, methods are not exposed to networking
+ /// (and RPCs).
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
+ public class RPCAttribute : Attribute
+ {
+ /// <summary>
+ /// RPC mode for the annotated method.
+ /// </summary>
+ public RPCMode Mode { get; } = RPCMode.Disabled;
+
+ /// <summary>
+ /// If the method will also be called locally; otherwise, it is only called remotely.
+ /// </summary>
+ public bool CallLocal { get; set; } = false;
+
+ /// <summary>
+ /// Transfer mode for the annotated method.
+ /// </summary>
+ public TransferMode TransferMode { get; set; } = TransferMode.Reliable;
+
+ /// <summary>
+ /// Transfer channel for the annotated mode.
+ /// </summary>
+ public int TransferChannel { get; set; } = 0;
+
+ /// <summary>
+ /// Constructs a <see cref="RPCAttribute"/> instance.
+ /// </summary>
+ /// <param name="mode">The RPC mode to use.</param>
+ public RPCAttribute(RPCMode mode = RPCMode.Authority)
+ {
+ Mode = mode;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
deleted file mode 100644
index b8b9bc660c..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System;
-
-namespace Godot
-{
- [AttributeUsage(AttributeTargets.Method)]
- public class AnyPeerAttribute : Attribute { }
-
- [AttributeUsage(AttributeTargets.Method)]
- public class AuthorityAttribute : Attribute { }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
index 12eb1035c3..3ebb6612de 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -2,11 +2,18 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// An attribute that contains the path to the object's script.
+ /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class ScriptPathAttribute : Attribute
{
private string path;
+ /// <summary>
+ /// Constructs a new ScriptPathAttribute instance.
+ /// </summary>
+ /// <param name="path">The file path to the script</param>
public ScriptPathAttribute(string path)
{
this.path = path;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
index 072e0f20ff..6475237002 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dispatcher.cs
@@ -4,9 +4,16 @@ namespace Godot
{
public static class Dispatcher
{
+ /// <summary>
+ /// Implements an external instance of GodotTaskScheduler.
+ /// </summary>
+ /// <returns>A GodotTaskScheduler instance.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern GodotTaskScheduler godot_icall_DefaultGodotTaskScheduler();
+ /// <summary>
+ /// Initializes the synchronization context as the context of the GodotTaskScheduler.
+ /// </summary>
public static GodotSynchronizationContext SynchronizationContext =>
godot_icall_DefaultGodotTaskScheduler().Context;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 74aa38386f..bb076a9633 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -239,6 +239,27 @@ namespace Godot
}
/// <summary>
+ /// Converts one or more arguments of any type to string in the best way possible and prints them to the console. The following BBCode tags are supported: b, i, u, s, indent, code, url, center, right, color, bgcolor, fgcolor. Color tags only support named colors such as [code]red[/code], [i]not[/i] hexadecimal color codes. Unsupported tags will be left as-is in standard output.
+ /// When printing to standard output, the supported subset of BBCode is converted to ANSI escape codes for the terminal emulator to display. Displaying ANSI escape codes is currently only supported on Linux and macOS. Support for ANSI escape codes may vary across terminal emulators, especially for italic and strikethrough.
+ ///
+ /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// to print error and warning messages instead of <see cref="Print(object[])"/> or <see cref="PrintRich(object[])"/>.
+ /// This distinguishes them from print messages used for debugging purposes,
+ /// while also displaying a stack trace when an error or warning is printed.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintRich("[b]Hello world![/b]"); // Prints out "Hello world!" in bold.
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
+ /// </summary>
+ public static void PrintRich(params object[] what)
+ {
+ godot_icall_GD_print_rich(GetPrintParams(what));
+ }
+
+ /// <summary>
/// Prints the current stack trace information to the console.
/// </summary>
public static void PrintStack()
@@ -562,6 +583,9 @@ namespace Godot
internal static extern void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void godot_icall_GD_print_rich(object[] what);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index c01c926e82..1b599beab5 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -14,6 +14,9 @@ namespace Godot
_queue.Add(new KeyValuePair<SendOrPostCallback, object>(d, state));
}
+ /// <summary>
+ /// Calls the Key method on each workItem object in the _queue to activate their callbacks.
+ /// </summary>
public void ExecutePendingContinuations()
{
while (_queue.TryTake(out var workItem))
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
index 8eaeea50dc..408bed71b2 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTaskScheduler.cs
@@ -6,11 +6,25 @@ using System.Threading.Tasks;
namespace Godot
{
+ /// <summary>
+ /// GodotTaskScheduler contains a linked list of tasks to perform as a queue. Methods
+ /// within the class are used to control the queue and perform the contained tasks.
+ /// </summary>
public class GodotTaskScheduler : TaskScheduler
{
+ /// <summary>
+ /// The current synchronization context.
+ /// </summary>
internal GodotSynchronizationContext Context { get; }
+
+ /// <summary>
+ /// The queue of tasks for the task scheduler.
+ /// </summary>
private readonly LinkedList<Task> _tasks = new LinkedList<Task>();
+ /// <summary>
+ /// Constructs a new GodotTaskScheduler instance.
+ /// </summary>
public GodotTaskScheduler()
{
Context = new GodotSynchronizationContext();
@@ -53,12 +67,19 @@ namespace Godot
}
}
+ /// <summary>
+ /// Executes all queued tasks and pending tasks from the current context.
+ /// </summary>
public void Activate()
{
ExecuteQueuedTasks();
Context.ExecutePendingContinuations();
}
+ /// <summary>
+ /// Loops through and attempts to execute each task in _tasks.
+ /// </summary>
+ /// <exception cref="InvalidOperationException"></exception>
private void ExecuteQueuedTasks()
{
while (true)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
index 0397957d00..e747e03c1e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaitable.cs
@@ -1,10 +1,17 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
public interface IAwaitable
{
IAwaiter GetAwaiter();
}
+ /// <summary>
+ /// A templated interface that requires a GetAwaiter() method to get a reference to the Awaiter.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
index d3be9d781c..dec225eb29 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/IAwaiter.cs
@@ -2,6 +2,9 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// An interface that requires a boolean for completion status and a method that gets the result of completion.
+ /// </summary>
public interface IAwaiter : INotifyCompletion
{
bool IsCompleted { get; }
@@ -9,6 +12,10 @@ namespace Godot
void GetResult();
}
+ /// <summary>
+ /// A templated interface that requires a boolean for completion status and a method that gets the result of completion and returns it.
+ /// </summary>
+ /// <typeparam name="TResult">A reference to the result to be passed out.</typeparam>
public interface IAwaiter<out TResult> : INotifyCompletion
{
bool IsCompleted { get; }
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
index c3fa2f3e82..90b4d1b8d3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Interfaces/ISerializationListener.cs
@@ -1,5 +1,8 @@
namespace Godot
{
+ /// <summary>
+ /// An interface that requires methods for before and after serialization.
+ /// </summary>
public interface ISerializationListener
{
void OnBeforeSerialize();
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
index ee4d0eed08..50ae2eb112 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
@@ -80,6 +80,11 @@ namespace Godot
private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
/// <summary>
+ /// Returns <see langword="true"/> if the <see cref="FlagsAttribute"/> is applied to the given type.
+ /// </summary>
+ private static bool TypeHasFlagsAttribute(Type type) => type.IsDefined(typeof(FlagsAttribute), false);
+
+ /// <summary>
/// Returns the generic type definition of <paramref name="type"/>.
/// </summary>
/// <exception cref="InvalidOperationException">
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index ce213da6a7..36b7d0f80f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -198,6 +198,28 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by
+ /// the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="start">The start value for the interpolation.</param>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination value for the interpolation.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting value of the interpolation.</returns>
+ public static real_t BezierInterpolate(real_t start, real_t control1, real_t control2, real_t end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return start * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Converts an angle expressed in degrees to radians.
/// </summary>
/// <param name="deg">An angle expressed in degrees.</param>
@@ -276,10 +298,14 @@ namespace Godot
/// Returns a normalized value considering the given range.
/// This is the opposite of <see cref="Lerp(real_t, real_t, real_t)"/>.
/// </summary>
- /// <param name="from">The interpolated value.</param>
+ /// <param name="from">The start value for interpolation.</param>
/// <param name="to">The destination value for interpolation.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting value of the inverse interpolation.</returns>
+ /// <param name="weight">The interpolated value.</param>
+ /// <returns>
+ /// The resulting value of the inverse interpolation.
+ /// The returned value will be between 0.0 and 1.0 if <paramref name="weight"/> is
+ /// between <paramref name="from"/> and <paramref name="to"/> (inclusive).
+ /// </returns>
public static real_t InverseLerp(real_t from, real_t to, real_t weight)
{
return (weight - from) / (to - from);
@@ -516,6 +542,21 @@ namespace Godot
}
/// <summary>
+ /// Maps a <paramref name="value"/> from [<paramref name="inFrom"/>, <paramref name="inTo"/>]
+ /// to [<paramref name="outFrom"/>, <paramref name="outTo"/>].
+ /// </summary>
+ /// <param name="value">The value to map.</param>
+ /// <param name="inFrom">The start value for the input interpolation.</param>
+ /// <param name="inTo">The destination value for the input interpolation.</param>
+ /// <param name="outFrom">The start value for the output interpolation.</param>
+ /// <param name="outTo">The destination value for the output interpolation.</param>
+ /// <returns>The resulting mapped value mapped.</returns>
+ public static real_t RangeLerp(real_t value, real_t inFrom, real_t inTo, real_t outFrom, real_t outTo)
+ {
+ return Lerp(outFrom, outTo, InverseLerp(inFrom, inTo, value));
+ }
+
+ /// <summary>
/// Rounds <paramref name="s"/> to the nearest whole number,
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 40fb5f8788..9ae01016cb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -170,6 +170,21 @@ namespace Godot
}
/// <summary>
+ /// Returns all names concatenated with a slash character (<c>/</c>).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
+ /// GD.Print(nodepath.GetConcatenatedNames()); // Path2D/PathFollow2D/Sprite2D
+ /// </code>
+ /// </example>
+ /// <returns>The names concatenated with <c>/</c>.</returns>
+ public string GetConcatenatedNames()
+ {
+ return godot_icall_NodePath_get_concatenated_names(GetPtr(this));
+ }
+
+ /// <summary>
/// Returns all subnames concatenated with a colon character (<c>:</c>)
/// as separator, i.e. the right side of the first colon in a node path.
/// </summary>
@@ -269,6 +284,9 @@ namespace Godot
private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 9e990ce83e..7bdbe1c28b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -221,6 +221,27 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination vector.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
+ public Vector2 BezierInterpolate(Vector2 control1, Vector2 control2, Vector2 end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
/// <param name="to">The other vector to point towards.</param>
@@ -522,9 +543,10 @@ namespace Godot
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
- if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
- // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
- return Lerp(to, weight);
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0)
+ {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
real_t startLength = Mathf.Sqrt(startLengthSquared);
real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index 412a885daa..b61954a84c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -461,7 +461,7 @@ namespace Godot
}
/// <summary>
- /// Multiplies each component of the <see cref="Vector2i"/>
+ /// Divides each component of the <see cref="Vector2i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 56859da7f2..480165d44a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -214,6 +214,27 @@ namespace Godot
}
/// <summary>
+ /// Returns the point at the given <paramref name="t"/> on a one-dimensional Bezier curve defined by this vector
+ /// and the given <paramref name="control1"/>, <paramref name="control2"/> and <paramref name="end"/> points.
+ /// </summary>
+ /// <param name="control1">Control point that defines the bezier curve.</param>
+ /// <param name="control2">Control point that defines the bezier curve.</param>
+ /// <param name="end">The destination vector.</param>
+ /// <param name="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The interpolated vector.</returns>
+ public Vector3 BezierInterpolate(Vector3 control1, Vector3 control2, Vector3 end, real_t t)
+ {
+ // Formula from Wikipedia article on Bezier curves
+ real_t omt = 1 - t;
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = t * t;
+ real_t t3 = t2 * t;
+
+ return this * omt3 + control1 * omt2 * t * 3 + control2 * omt * t2 * 3 + end * t3;
+ }
+
+ /// <summary>
/// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
/// <param name="to">The other vector to point towards.</param>
@@ -562,9 +583,10 @@ namespace Godot
{
real_t startLengthSquared = LengthSquared();
real_t endLengthSquared = to.LengthSquared();
- if (startLengthSquared == 0.0 || endLengthSquared == 0.0) {
- // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
- return Lerp(to, weight);
+ if (startLengthSquared == 0.0 || endLengthSquared == 0.0)
+ {
+ // Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
+ return Lerp(to, weight);
}
real_t startLength = Mathf.Sqrt(startLengthSquared);
real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index abfd2ae720..0d4894f206 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -449,7 +449,7 @@ namespace Godot
}
/// <summary>
- /// Multiplies each component of the <see cref="Vector3i"/>
+ /// Divides each component of the <see cref="Vector3i"/>
/// by the given <see langword="int"/>.
/// </summary>
/// <param name="vec">The dividend vector.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 1fcfe74c86..e59f45bbf6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -18,7 +18,7 @@
<Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
<Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
- <Compile Include="Core\Attributes\RPCAttributes.cs" />
+ <Compile Include="Core\Attributes\RPCAttribute.cs" />
<Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index b10d78c593..7b9dbc87cf 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -68,7 +68,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
@@ -115,7 +115,7 @@ void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoole
void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->get();
if (script_binding.inited) {
MonoGCHandleData &gchandle = script_binding.gchandle;
if (!gchandle.is_released()) {
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 8aead217cf..8b1c2b729e 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -90,6 +90,27 @@ void godot_icall_GD_print(MonoArray *p_what) {
print_line(str);
}
+void godot_icall_GD_print_rich(MonoArray *p_what) {
+ String str;
+ int length = mono_array_length(p_what);
+
+ for (int i = 0; i < length; i++) {
+ MonoObject *elem = mono_array_get(p_what, MonoObject *, i);
+
+ MonoException *exc = nullptr;
+ String elem_str = GDMonoMarshal::mono_object_to_variant_string(elem, &exc);
+
+ if (exc) {
+ GDMonoUtils::set_pending_exception(exc);
+ return;
+ }
+
+ str += elem_str;
+ }
+
+ print_line_rich(str);
+}
+
void godot_icall_GD_printerr(MonoArray *p_what) {
String str;
int length = mono_array_length(p_what);
@@ -300,6 +321,7 @@ void godot_register_gd_icalls() {
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print_rich", godot_icall_GD_print_rich);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index 0ea9814b1a..16e1509eb0 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -68,6 +68,10 @@ MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx));
}
+MonoString *godot_icall_NodePath_get_concatenated_names(NodePath *p_ptr) {
+ return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_names());
+}
+
MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) {
return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames());
}
@@ -85,6 +89,7 @@ void godot_register_nodepath_icalls() {
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);
GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count);
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index 8ed21c323f..c159bb9eea 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -36,7 +36,7 @@
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable>::List ManagedCallable::instances;
-Map<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
+RBMap<ManagedCallable *, Array> ManagedCallable::instances_pending_reload;
Mutex ManagedCallable::instances_mutex;
#endif
@@ -66,9 +66,8 @@ bool ManagedCallable::compare_less(const CallableCustom *p_a, const CallableCust
}
uint32_t ManagedCallable::hash() const {
- // hmm
uint32_t hash = delegate_invoke->get_name().hash();
- return hash_djb2_one_64(delegate_handle.handle, hash);
+ return hash_murmur3_one_64(delegate_handle.handle, hash);
}
String ManagedCallable::get_as_text() const {
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index d50a8a7b08..11bee6cf60 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -48,7 +48,7 @@ class ManagedCallable : public CallableCustom {
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
static SelfList<ManagedCallable>::List instances;
- static Map<ManagedCallable *, Array> instances_pending_reload;
+ static RBMap<ManagedCallable *, Array> instances_pending_reload;
static Mutex instances_mutex;
#endif
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 3991b14612..42c6b6305f 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -412,10 +412,10 @@ GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const Stri
GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
ERR_FAIL_NULL_V(image, nullptr);
- Map<MonoClass *, GDMonoClass *>::Element *match = cached_raw.find(p_mono_class);
+ HashMap<MonoClass *, GDMonoClass *>::Iterator match = cached_raw.find(p_mono_class);
if (match) {
- return match->value();
+ return match->value;
}
StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index a96357b082..0a3ae6c4fe 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -36,7 +36,7 @@
#include "core/string/ustring.h"
#include "core/templates/hash_map.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "gd_mono_utils.h"
class GDMonoAssembly {
@@ -79,7 +79,7 @@ class GDMonoAssembly {
#endif
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
- Map<MonoClass *, GDMonoClass *> cached_raw;
+ HashMap<MonoClass *, GDMonoClass *> cached_raw;
static Vector<String> search_dirs;
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index d8fd244067..fd78fae4ad 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -140,8 +140,11 @@ void CachedData::clear_godot_api_cache() {
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
- class_AnyPeerAttribute = nullptr;
- class_AuthorityAttribute = nullptr;
+ class_RPCAttribute = nullptr;
+ property_RPCAttribute_Mode = nullptr;
+ property_RPCAttribute_CallLocal = nullptr;
+ property_RPCAttribute_TransferMode = nullptr;
+ property_RPCAttribute_TransferChannel = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
class_ScriptPathAttribute = nullptr;
@@ -176,6 +179,7 @@ void CachedData::clear_godot_api_cache() {
methodthunk_MarshalUtils_TypeIsGenericIEnumerable.nullify();
methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
+ methodthunk_MarshalUtils_TypeHasFlagsAttribute.nullify();
methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
@@ -267,8 +271,11 @@ void update_godot_api_cache() {
CACHE_FIELD_AND_CHECK(ExportAttribute, hintString, CACHED_CLASS(ExportAttribute)->get_field("hintString"));
CACHE_CLASS_AND_CHECK(SignalAttribute, GODOT_API_CLASS(SignalAttribute));
CACHE_CLASS_AND_CHECK(ToolAttribute, GODOT_API_CLASS(ToolAttribute));
- CACHE_CLASS_AND_CHECK(AnyPeerAttribute, GODOT_API_CLASS(AnyPeerAttribute));
- CACHE_CLASS_AND_CHECK(AuthorityAttribute, GODOT_API_CLASS(AuthorityAttribute));
+ CACHE_CLASS_AND_CHECK(RPCAttribute, GODOT_API_CLASS(RPCAttribute));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, Mode, CACHED_CLASS(RPCAttribute)->get_property("Mode"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, CallLocal, CACHED_CLASS(RPCAttribute)->get_property("CallLocal"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferMode, CACHED_CLASS(RPCAttribute)->get_property("TransferMode"));
+ CACHE_PROPERTY_AND_CHECK(RPCAttribute, TransferChannel, CACHED_CLASS(RPCAttribute)->get_property("TransferChannel"));
CACHE_CLASS_AND_CHECK(GodotMethodAttribute, GODOT_API_CLASS(GodotMethodAttribute));
CACHE_FIELD_AND_CHECK(GodotMethodAttribute, methodName, CACHED_CLASS(GodotMethodAttribute)->get_field("methodName"));
CACHE_CLASS_AND_CHECK(ScriptPathAttribute, GODOT_API_CLASS(ScriptPathAttribute));
@@ -300,6 +307,7 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIEnumerable, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIEnumerable", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericICollection, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericICollection", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeIsGenericIDictionary, GODOT_API_CLASS(MarshalUtils)->get_method("TypeIsGenericIDictionary", 1));
+ CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, TypeHasFlagsAttribute, GODOT_API_CLASS(MarshalUtils)->get_method("TypeHasFlagsAttribute", 1));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 4000342c94..b3b0865608 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -111,8 +111,11 @@ struct CachedData {
GDMonoField *field_ExportAttribute_hintString = nullptr;
GDMonoClass *class_SignalAttribute = nullptr;
GDMonoClass *class_ToolAttribute = nullptr;
- GDMonoClass *class_AnyPeerAttribute = nullptr;
- GDMonoClass *class_AuthorityAttribute = nullptr;
+ GDMonoClass *class_RPCAttribute = nullptr;
+ GDMonoProperty *property_RPCAttribute_Mode = nullptr;
+ GDMonoProperty *property_RPCAttribute_CallLocal = nullptr;
+ GDMonoProperty *property_RPCAttribute_TransferMode = nullptr;
+ GDMonoProperty *property_RPCAttribute_TransferChannel = nullptr;
GDMonoClass *class_GodotMethodAttribute = nullptr;
GDMonoField *field_GodotMethodAttribute_methodName = nullptr;
GDMonoClass *class_ScriptPathAttribute = nullptr;
@@ -147,6 +150,7 @@ struct CachedData {
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIEnumerable;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
+ GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeHasFlagsAttribute;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index daf70443e9..51c5aa3542 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -360,10 +360,10 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
}
GDMonoField *GDMonoClass::get_field(const StringName &p_name) {
- Map<StringName, GDMonoField *>::Element *result = fields.find(p_name);
+ HashMap<StringName, GDMonoField *>::Iterator result = fields.find(p_name);
if (result) {
- return result->value();
+ return result->value;
}
if (fields_fetched) {
@@ -392,10 +392,10 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_field_get_name(raw_field));
- Map<StringName, GDMonoField *>::Element *match = fields.find(name);
+ HashMap<StringName, GDMonoField *>::Iterator match = fields.find(name);
if (match) {
- fields_list.push_back(match->get());
+ fields_list.push_back(match->value);
} else {
GDMonoField *field = memnew(GDMonoField(raw_field, this));
fields.insert(name, field);
@@ -409,10 +409,10 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
}
GDMonoProperty *GDMonoClass::get_property(const StringName &p_name) {
- Map<StringName, GDMonoProperty *>::Element *result = properties.find(p_name);
+ HashMap<StringName, GDMonoProperty *>::Iterator result = properties.find(p_name);
if (result) {
- return result->value();
+ return result->value;
}
if (properties_fetched) {
@@ -441,10 +441,10 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
StringName name = String::utf8(mono_property_get_name(raw_property));
- Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
+ HashMap<StringName, GDMonoProperty *>::Iterator match = properties.find(name);
if (match) {
- properties_list.push_back(match->get());
+ properties_list.push_back(match->value);
} else {
GDMonoProperty *property = memnew(GDMonoProperty(raw_property, this));
properties.insert(name, property);
@@ -477,10 +477,10 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
if (mono_class_is_delegate(raw_class)) {
StringName name = String::utf8(mono_class_get_name(raw_class));
- Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
+ HashMap<StringName, GDMonoClass *>::Iterator match = delegates.find(name);
if (match) {
- delegates_list.push_back(match->get());
+ delegates_list.push_back(match->value);
} else {
GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly));
delegates.insert(name, delegate);
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index b32d561f61..6b35da30f9 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -32,7 +32,7 @@
#define GD_MONO_CLASS_H
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
@@ -85,15 +85,15 @@ class GDMonoClass {
Vector<GDMonoMethod *> method_list;
bool fields_fetched;
- Map<StringName, GDMonoField *> fields;
+ HashMap<StringName, GDMonoField *> fields;
Vector<GDMonoField *> fields_list;
bool properties_fetched;
- Map<StringName, GDMonoProperty *> properties;
+ HashMap<StringName, GDMonoProperty *> properties;
Vector<GDMonoProperty *> properties_list;
bool delegates_fetched;
- Map<StringName, GDMonoClass *> delegates;
+ HashMap<StringName, GDMonoClass *> delegates;
Vector<GDMonoClass *> delegates_list;
friend class GDMonoAssembly;
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index a884bf4da0..1983d6ebe2 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -70,7 +70,7 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
void *data = CSharpLanguage::get_instance_binding(unmanaged);
ERR_FAIL_NULL_V(data, nullptr);
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
+ CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)data)->value();
ERR_FAIL_COND_V(!script_binding.inited, nullptr);
MonoGCHandleData &gchandle = script_binding.gchandle;
@@ -614,6 +614,14 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
return (bool)res;
}
+bool type_has_flags_attribute(MonoReflectionType *p_reftype) {
+ NO_GLUE_RET(false);
+ MonoException *exc = nullptr;
+ MonoBoolean res = CACHED_METHOD_THUNK(MarshalUtils, TypeHasFlagsAttribute).invoke(p_reftype, &exc);
+ UNHANDLED_EXCEPTION(exc);
+ return (bool)res;
+}
+
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, GetGenericTypeDefinition).invoke(p_reftype, r_generic_reftype, &exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 4c2c2c93c2..246a1cd31e 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -61,6 +61,7 @@ bool type_is_system_generic_dictionary(MonoReflectionType *p_reftype);
bool type_is_generic_ienumerable(MonoReflectionType *p_reftype);
bool type_is_generic_icollection(MonoReflectionType *p_reftype);
bool type_is_generic_idictionary(MonoReflectionType *p_reftype);
+bool type_has_flags_attribute(MonoReflectionType *p_reftype);
void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype);
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index 315a9c29f6..618e1b58e0 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -63,7 +63,7 @@ bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const Callab
uint32_t SignalAwaiterCallable::hash() const {
uint32_t hash = signal.hash();
- return hash_djb2_one_64(target_id, hash);
+ return hash_murmur3_one_64(target_id, hash);
}
String SignalAwaiterCallable::get_as_text() const {
@@ -164,7 +164,7 @@ bool EventSignalCallable::compare_less(const CallableCustom *p_a, const Callable
uint32_t EventSignalCallable::hash() const {
uint32_t hash = event_signal->field->get_name().hash();
- return hash_djb2_one_64(owner->get_instance_id(), hash);
+ return hash_murmur3_one_64(owner->get_instance_id(), hash);
}
String EventSignalCallable::get_as_text() const {
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index 15a0b28181..a1905dfcfe 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -62,7 +62,8 @@ String cwd() {
}
String result;
- if (result.parse_utf16(buffer.ptr())) {
+ result.parse_utf16(buffer.ptr());
+ if (result.is_empty()) {
return ".";
}
return result.simplify_path();
@@ -73,7 +74,7 @@ String cwd() {
}
String result;
- if (result.parse_utf8(buffer)) {
+ if (result.parse_utf8(buffer) != OK) {
return ".";
}
@@ -114,7 +115,8 @@ String realpath(const String &p_path) {
::CloseHandle(hFile);
String result;
- if (result.parse_utf16(buffer.ptr())) {
+ result.parse_utf16(buffer.ptr());
+ if (result.is_empty()) {
return p_path;
}
@@ -127,10 +129,10 @@ String realpath(const String &p_path) {
}
String result;
- bool parse_ok = result.parse_utf8(resolved_path);
+ Error parse_ok = result.parse_utf8(resolved_path);
::free(resolved_path);
- if (parse_ok) {
+ if (parse_ok != OK) {
return p_path;
}
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index e6975611d2..975f2d8332 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -145,7 +145,7 @@ bool is_csharp_keyword(const String &p_name) {
p_name == "do" || p_name == "double" || p_name == "else" || p_name == "enum" ||
p_name == "event" || p_name == "explicit" || p_name == "extern" || p_name == "false" ||
p_name == "finally" || p_name == "fixed" || p_name == "float" || p_name == "for" ||
- p_name == "forech" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
+ p_name == "foreach" || p_name == "goto" || p_name == "if" || p_name == "implicit" ||
p_name == "in" || p_name == "int" || p_name == "interface" || p_name == "internal" ||
p_name == "is" || p_name == "lock" || p_name == "long" || p_name == "namespace" ||
p_name == "new" || p_name == "null" || p_name == "object" || p_name == "operator" ||
@@ -178,7 +178,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
w[len] = 0;
String source;
- if (source.parse_utf8((const char *)w)) {
+ if (source.parse_utf8((const char *)w) != OK) {
ERR_FAIL_V(ERR_INVALID_DATA);
}
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index d16d41b438..bcbc721dbb 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -123,6 +123,18 @@ void GodotNavigationServer::add_command(SetCommand *command) const {
}
}
+Array GodotNavigationServer::get_maps() const {
+ Array all_map_rids;
+ List<RID> maps_owned;
+ map_owner.get_owned_list(&maps_owned);
+ if (maps_owned.size()) {
+ for (const RID &E : maps_owned) {
+ all_map_rids.push_back(E);
+ }
+ }
+ return all_map_rids;
+}
+
RID GodotNavigationServer::map_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
@@ -198,11 +210,11 @@ real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
return map->get_edge_connection_margin();
}
-Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> GodotNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
- return map->get_path(p_origin, p_destination, p_optimize, p_layers);
+ return map->get_path(p_origin, p_destination, p_optimize, p_navigation_layers);
}
Vector3 GodotNavigationServer::map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@@ -233,6 +245,44 @@ RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3
return map->get_closest_point_owner(p_point);
}
+Array GodotNavigationServer::map_get_regions(RID p_map) const {
+ Array regions_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, regions_rids);
+ for (NavRegion *region : map->get_regions()) {
+ regions_rids.push_back(region->get_self());
+ }
+ return regions_rids;
+}
+
+Array GodotNavigationServer::map_get_agents(RID p_map) const {
+ Array agents_rids;
+ const NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND_V(map == nullptr, agents_rids);
+ for (RvoAgent *agent : map->get_agents()) {
+ agents_rids.push_back(agent->get_self());
+ }
+ return agents_rids;
+}
+
+RID GodotNavigationServer::region_get_map(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, RID());
+ if (region->get_map()) {
+ return region->get_map()->get_self();
+ }
+ return RID();
+}
+
+RID GodotNavigationServer::agent_get_map(RID p_agent) const {
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
+ ERR_FAIL_COND_V(agent == nullptr, RID());
+ if (agent->get_map()) {
+ return agent->get_map()->get_self();
+ }
+ return RID();
+}
+
RID GodotNavigationServer::region_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
@@ -271,18 +321,58 @@ COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
region->set_transform(p_transform);
}
-COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers) {
+COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost) {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_enter_cost < 0.0);
- region->set_layers(p_layers);
+ region->set_enter_cost(p_enter_cost);
}
-uint32_t GodotNavigationServer::region_get_layers(RID p_region) const {
+real_t GodotNavigationServer::region_get_enter_cost(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
- return region->get_layers();
+ return region->get_enter_cost();
+}
+
+COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+ ERR_FAIL_COND(p_travel_cost < 0.0);
+
+ region->set_travel_cost(p_travel_cost);
+}
+
+real_t GodotNavigationServer::region_get_travel_cost(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_travel_cost();
+}
+
+bool GodotNavigationServer::region_owns_point(RID p_region, const Vector3 &p_point) const {
+ const NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, false);
+ if (region->get_map()) {
+ RID closest_point_owner = map_get_closest_point_owner(region->get_map()->get_self(), p_point);
+ return closest_point_owner == region->get_self();
+ }
+ return false;
+}
+
+COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers) {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND(region == nullptr);
+
+ region->set_navigation_layers(p_navigation_layers);
+}
+
+uint32_t GodotNavigationServer::region_get_navigation_layers(RID p_region) const {
+ NavRegion *region = region_owner.get_or_null(p_region);
+ ERR_FAIL_COND_V(region == nullptr, 0);
+
+ return region->get_navigation_layers();
}
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
@@ -512,6 +602,15 @@ void GodotNavigationServer::flush_queries() {
commands.clear();
}
+void GodotNavigationServer::map_force_update(RID p_map) {
+ NavMap *map = map_owner.get_or_null(p_map);
+ ERR_FAIL_COND(map == nullptr);
+
+ flush_queries();
+
+ map->sync();
+}
+
void GodotNavigationServer::process(real_t p_delta_time) {
flush_queries();
diff --git a/modules/navigation/godot_navigation_server.h b/modules/navigation/godot_navigation_server.h
index 7ad5e2d501..8e7e99888c 100644
--- a/modules/navigation/godot_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -85,6 +85,8 @@ public:
void add_command(SetCommand *command) const;
+ virtual Array get_maps() const override;
+
virtual RID map_create() const override;
COMMAND_2(map_set_active, RID, p_map, bool, p_active);
virtual bool map_is_active(RID p_map) const override;
@@ -98,17 +100,31 @@ public:
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const override;
- virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const override;
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const override;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const override;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const override;
virtual Vector3 map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const override;
virtual RID map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const override;
+ virtual Array map_get_regions(RID p_map) const override;
+ virtual Array map_get_agents(RID p_map) const override;
+
+ virtual void map_force_update(RID p_map) override;
+
virtual RID region_create() const override;
+
+ COMMAND_2(region_set_enter_cost, RID, p_region, real_t, p_enter_cost);
+ virtual real_t region_get_enter_cost(RID p_region) const override;
+ COMMAND_2(region_set_travel_cost, RID, p_region, real_t, p_travel_cost);
+ virtual real_t region_get_travel_cost(RID p_region) const override;
+
+ virtual bool region_owns_point(RID p_region, const Vector3 &p_point) const override;
+
COMMAND_2(region_set_map, RID, p_region, RID, p_map);
- COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers);
- virtual uint32_t region_get_layers(RID p_region) const override;
+ virtual RID region_get_map(RID p_region) const override;
+ COMMAND_2(region_set_navigation_layers, RID, p_region, uint32_t, p_navigation_layers);
+ virtual uint32_t region_get_navigation_layers(RID p_region) const override;
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const override;
@@ -118,6 +134,7 @@ public:
virtual RID agent_create() const override;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
+ virtual RID agent_get_map(RID p_agent) const override;
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist);
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count);
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time);
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index cbc0adc574..17d6e0a0a1 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -65,7 +65,7 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
return p;
}
-Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers) const {
// Find the start poly and the end poly on this map.
const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
@@ -78,7 +78,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Polygon &p = polygons[i];
// Only consider the polygon if it in a region with compatible layers.
- if ((p_layers & p.owner->get_layers()) == 0) {
+ if ((p_navigation_layers & p.owner->get_navigation_layers()) == 0) {
continue;
}
@@ -140,6 +140,8 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
float reachable_d = 1e30;
bool is_reachable = true;
+ gd::NavigationPoly *prev_least_cost_poly = nullptr;
+
while (true) {
// Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
@@ -152,13 +154,21 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
const gd::Edge::Connection &connection = edge.connections[connection_index];
// Only consider the connection to another polygon if this polygon is in a region with compatible layers.
- if ((p_layers & connection.polygon->owner->get_layers()) == 0) {
+ if ((p_navigation_layers & connection.polygon->owner->get_navigation_layers()) == 0) {
continue;
}
+ float region_enter_cost = 0.0;
+ float region_travel_cost = least_cost_poly->poly->owner->get_travel_cost();
+
+ if (prev_least_cost_poly != nullptr && !(prev_least_cost_poly->poly->owner->get_self() == least_cost_poly->poly->owner->get_self())) {
+ region_enter_cost = least_cost_poly->poly->owner->get_enter_cost();
+ }
+ prev_least_cost_poly = least_cost_poly;
+
Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway);
- const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
+ const float new_distance = (least_cost_poly->entry.distance_to(new_entry) * region_travel_cost) + region_enter_cost + least_cost_poly->traveled_distance;
const std::vector<gd::NavigationPoly>::iterator it = std::find(
navigation_polys.begin(),
@@ -238,7 +248,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
gd::NavigationPoly *np = &navigation_polys[element->get()];
float cost = np->traveled_distance;
- cost += np->entry.distance_to(end_point);
+ cost += (np->entry.distance_to(end_point) * np->poly->owner->get_travel_cost());
if (cost < least_cost) {
least_cost_id = np->self_id;
least_cost = cost;
@@ -249,7 +259,7 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Stores the further reachable end polygon, in case our goal is not reachable.
if (is_reachable) {
- float d = navigation_polys[least_cost_id].entry.distance_to(p_destination);
+ float d = navigation_polys[least_cost_id].entry.distance_to(p_destination) * navigation_polys[least_cost_id].poly->owner->get_travel_cost();
if (reachable_d > d) {
reachable_d = d;
reachable_end = navigation_polys[least_cost_id].poly;
@@ -541,7 +551,7 @@ void NavMap::sync() {
}
// Group all edges per key.
- Map<gd::EdgeKey, Vector<gd::Edge::Connection>> connections;
+ HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
gd::Polygon &poly(polygons[poly_id]);
@@ -549,7 +559,7 @@ void NavMap::sync() {
int next_point = (p + 1) % poly.points.size();
gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
- Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *connection = connections.find(ek);
+ HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
if (!connection) {
connections[ek] = Vector<gd::Edge::Connection>();
}
@@ -563,7 +573,7 @@ void NavMap::sync() {
connections[ek].push_back(new_connection);
} else {
// The edge is already connected with another edge, skip.
- ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
+ ERR_PRINT_ONCE("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problems.");
}
}
}
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index 5232e42bed..2036dbecd7 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RVO_SPACE_H
-#define RVO_SPACE_H
+#ifndef NAV_MAP_H
+#define NAV_MAP_H
#include "nav_rid.h"
#include "core/math/math_defs.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/thread_work_pool.h"
#include "nav_utils.h"
@@ -50,10 +50,10 @@ class NavMap : public NavRid {
/// To find the polygons edges the vertices are displaced in a grid where
/// each cell has the following cell_size.
- real_t cell_size = 0.3;
+ real_t cell_size = 0.25;
/// This value is used to detect the near edges to connect.
- real_t edge_connection_margin = 5.0;
+ real_t edge_connection_margin = 0.25;
bool regenerate_polygons = true;
bool regenerate_links = true;
@@ -105,7 +105,7 @@ public:
gd::PointKey get_point_key(const Vector3 &p_pos) const;
- Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_navigation_layers = 1) const;
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const;
Vector3 get_closest_point(const Vector3 &p_point) const;
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
@@ -141,4 +141,4 @@ private:
void clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const;
};
-#endif // RVO_SPACE_H
+#endif // NAV_MAP_H
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index fea0ad519a..88740807eb 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -40,12 +40,12 @@ void NavRegion::set_map(NavMap *p_map) {
}
}
-void NavRegion::set_layers(uint32_t p_layers) {
- layers = p_layers;
+void NavRegion::set_navigation_layers(uint32_t p_navigation_layers) {
+ navigation_layers = p_navigation_layers;
}
-uint32_t NavRegion::get_layers() const {
- return layers;
+uint32_t NavRegion::get_navigation_layers() const {
+ return navigation_layers;
}
void NavRegion::set_transform(Transform3D p_transform) {
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index 7a6da281c0..484856ae36 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -45,7 +45,9 @@ class NavRegion : public NavRid {
NavMap *map = nullptr;
Transform3D transform;
Ref<NavigationMesh> mesh;
- uint32_t layers = 1;
+ uint32_t navigation_layers = 1;
+ float enter_cost = 0.0;
+ float travel_cost = 1.0;
Vector<gd::Edge::Connection> connections;
bool polygons_dirty = true;
@@ -65,8 +67,14 @@ public:
return map;
}
- void set_layers(uint32_t p_layers);
- uint32_t get_layers() const;
+ void set_enter_cost(float p_enter_cost) { enter_cost = MAX(p_enter_cost, 0.0); }
+ float get_enter_cost() const { return enter_cost; }
+
+ void set_travel_cost(float p_travel_cost) { travel_cost = MAX(p_travel_cost, 0.0); }
+ float get_travel_cost() const { return travel_cost; }
+
+ void set_navigation_layers(uint32_t p_navigation_layers);
+ uint32_t get_navigation_layers() const;
void set_transform(Transform3D transform);
const Transform3D &get_transform() const {
diff --git a/modules/navigation/nav_utils.h b/modules/navigation/nav_utils.h
index 5b6c695ca4..a9f4e0e2fc 100644
--- a/modules/navigation/nav_utils.h
+++ b/modules/navigation/nav_utils.h
@@ -32,8 +32,9 @@
#define NAV_UTILS_H
#include "core/math/vector3.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/hashfuncs.h"
#include "core/templates/vector.h"
-
#include <vector>
class NavRegion;
@@ -49,15 +50,18 @@ union PointKey {
};
uint64_t key = 0;
- bool operator<(const PointKey &p_key) const { return key < p_key.key; }
};
struct EdgeKey {
PointKey a;
PointKey b;
- bool operator<(const EdgeKey &p_key) const {
- return (a.key == p_key.a.key) ? (b.key < p_key.b.key) : (a.key < p_key.a.key);
+ static uint32_t hash(const EdgeKey &p_val) {
+ return hash_one_uint64(p_val.a.key) ^ hash_one_uint64(p_val.b.key);
+ }
+
+ bool operator==(const EdgeKey &p_key) const {
+ return (a.key == p_key.a.key) && (b.key == p_key.b.key);
}
EdgeKey(const PointKey &p_a = PointKey(), const PointKey &p_b = PointKey()) :
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 110a4f5138..6e8ac77f79 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -172,14 +172,16 @@ void NavigationMeshGenerator::_parse_geometry(const Transform3D &p_navmesh_trans
if (Object::cast_to<MultiMeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MultiMeshInstance3D *multimesh_instance = Object::cast_to<MultiMeshInstance3D>(p_node);
Ref<MultiMesh> multimesh = multimesh_instance->get_multimesh();
- Ref<Mesh> mesh = multimesh->get_mesh();
- if (mesh.is_valid()) {
- int n = multimesh->get_visible_instance_count();
- if (n == -1) {
- n = multimesh->get_instance_count();
- }
- for (int i = 0; i < n; i++) {
- _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ if (multimesh.is_valid()) {
+ Ref<Mesh> mesh = multimesh->get_mesh();
+ if (mesh.is_valid()) {
+ int n = multimesh->get_visible_instance_count();
+ if (n == -1) {
+ n = multimesh->get_instance_count();
+ }
+ for (int i = 0; i < n; i++) {
+ _add_mesh(mesh, p_navmesh_transform * multimesh_instance->get_global_transform() * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ }
}
}
}
@@ -445,9 +447,34 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
cfg.minRegionArea = (int)(p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size());
cfg.mergeRegionArea = (int)(p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size());
cfg.maxVertsPerPoly = (int)p_nav_mesh->get_verts_per_poly();
- cfg.detailSampleDist = p_nav_mesh->get_detail_sample_distance() < 0.9f ? 0 : p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance();
+ cfg.detailSampleDist = MAX(p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance(), 0.1f);
cfg.detailSampleMaxError = p_nav_mesh->get_cell_height() * p_nav_mesh->get_detail_sample_max_error();
+ if (!Math::is_equal_approx((float)cfg.walkableHeight * cfg.ch, p_nav_mesh->get_agent_height())) {
+ WARN_PRINT("Property agent_height is ceiled to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableClimb * cfg.ch, p_nav_mesh->get_agent_max_climb())) {
+ WARN_PRINT("Property agent_max_climb is floored to cell_height voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.walkableRadius * cfg.cs, p_nav_mesh->get_agent_radius())) {
+ WARN_PRINT("Property agent_radius is ceiled to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxEdgeLen * cfg.cs, p_nav_mesh->get_edge_max_length())) {
+ WARN_PRINT("Property edge_max_length is rounded to cell_size voxel units and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.minRegionArea, p_nav_mesh->get_region_min_size() * p_nav_mesh->get_region_min_size())) {
+ WARN_PRINT("Property region_min_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.mergeRegionArea, p_nav_mesh->get_region_merge_size() * p_nav_mesh->get_region_merge_size())) {
+ WARN_PRINT("Property region_merge_size is converted to int and loses precision.");
+ }
+ if (!Math::is_equal_approx((float)cfg.maxVertsPerPoly, p_nav_mesh->get_verts_per_poly())) {
+ WARN_PRINT("Property verts_per_poly is converted to int and loses precision.");
+ }
+ if (p_nav_mesh->get_cell_size() * p_nav_mesh->get_detail_sample_distance() < 0.1f) {
+ WARN_PRINT("Property detail_sample_distance is clamped to 0.1 world units as the resulting value from multiplying with cell_size is too low.");
+ }
+
cfg.bmin[0] = bmin[0];
cfg.bmin[1] = bmin[1];
cfg.bmin[2] = bmin[2];
@@ -455,6 +482,21 @@ void NavigationMeshGenerator::_build_recast_navigation_mesh(
cfg.bmax[1] = bmax[1];
cfg.bmax[2] = bmax[2];
+ AABB baking_aabb = p_nav_mesh->get_filter_baking_aabb();
+
+ bool aabb_has_no_volume = baking_aabb.has_no_volume();
+
+ if (!aabb_has_no_volume) {
+ Vector3 baking_aabb_offset = p_nav_mesh->get_filter_baking_aabb_offset();
+
+ cfg.bmin[0] = baking_aabb.position[0] + baking_aabb_offset.x;
+ cfg.bmin[1] = baking_aabb.position[1] + baking_aabb_offset.y;
+ cfg.bmin[2] = baking_aabb.position[2] + baking_aabb_offset.z;
+ cfg.bmax[0] = cfg.bmin[0] + baking_aabb.size[0];
+ cfg.bmax[1] = cfg.bmin[1] + baking_aabb.size[1];
+ cfg.bmax[2] = cfg.bmin[2] + baking_aabb.size[2];
+ }
+
#ifdef TOOLS_ENABLED
if (ep) {
ep->step(TTR("Calculating grid size..."), 2);
@@ -604,6 +646,13 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
#ifdef TOOLS_ENABLED
EditorProgress *ep(nullptr);
+ // FIXME
+#endif
+#if 0
+ // After discussion on devchat disabled EditorProgress for now as it is not thread-safe and uses hacks and Main::iteration() for steps.
+ // EditorProgress randomly crashes the Engine when the bake function is used with a thread e.g. inside Editor with a tool script and procedural navigation
+ // This was not a problem in older versions as previously Godot was unable to (re)bake NavigationMesh at runtime.
+ // If EditorProgress is fixed and made thread-safe this should be enabled again.
if (Engine::get_singleton()->is_editor_hint()) {
ep = memnew(EditorProgress("bake", TTR("Navigation Mesh Generator Setup:"), 11));
}
diff --git a/modules/navigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp
index a6a5660c0c..4ec72ad43f 100644
--- a/modules/navigation/rvo_agent.cpp
+++ b/modules/navigation/rvo_agent.cpp
@@ -65,8 +65,9 @@ void RvoAgent::dispatch_callback() {
return;
}
Object *obj = ObjectDB::get_instance(callback.id);
- if (obj == nullptr) {
+ if (!obj) {
callback.id = ObjectID();
+ return;
}
Callable::CallError responseCallError;
diff --git a/modules/noise/editor/noise_editor_plugin.cpp b/modules/noise/editor/noise_editor_plugin.cpp
index 32c3f0aad4..27a86f45b5 100644
--- a/modules/noise/editor/noise_editor_plugin.cpp
+++ b/modules/noise/editor/noise_editor_plugin.cpp
@@ -54,7 +54,7 @@ public:
set_custom_minimum_size(Size2(0, EDSCALE * PREVIEW_HEIGHT));
_texture_rect = memnew(TextureRect);
- _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ _texture_rect->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
_texture_rect->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_COVERED);
add_child(_texture_rect);
diff --git a/modules/noise/fastnoise_lite.cpp b/modules/noise/fastnoise_lite.cpp
index a8d38dee62..b21e3247d7 100644
--- a/modules/noise/fastnoise_lite.cpp
+++ b/modules/noise/fastnoise_lite.cpp
@@ -30,6 +30,24 @@
#include "fastnoise_lite.h"
+_FastNoiseLite::FractalType FastNoiseLite::_convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type) {
+ _FastNoiseLite::FractalType type;
+ switch (p_domain_warp_fractal_type) {
+ case DOMAIN_WARP_FRACTAL_NONE:
+ type = _FastNoiseLite::FractalType_None;
+ break;
+ case DOMAIN_WARP_FRACTAL_PROGRESSIVE:
+ type = _FastNoiseLite::FractalType_DomainWarpProgressive;
+ break;
+ case DOMAIN_WARP_FRACTAL_INDEPENDENT:
+ type = _FastNoiseLite::FractalType_DomainWarpIndependent;
+ break;
+ default:
+ type = _FastNoiseLite::FractalType_None;
+ }
+ return type;
+}
+
FastNoiseLite::FastNoiseLite() {
_noise.SetNoiseType((_FastNoiseLite::NoiseType)noise_type);
_noise.SetSeed(seed);
@@ -50,7 +68,7 @@ FastNoiseLite::FastNoiseLite() {
_domain_warp_noise.SetSeed(seed);
_domain_warp_noise.SetDomainWarpAmp(domain_warp_amplitude);
_domain_warp_noise.SetFrequency(domain_warp_frequency);
- _domain_warp_noise.SetFractalType(_FastNoiseLite::FractalType_None);
+ _domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(domain_warp_fractal_type));
_domain_warp_noise.SetFractalOctaves(domain_warp_fractal_octaves);
_domain_warp_noise.SetFractalLacunarity(domain_warp_fractal_lacunarity);
_domain_warp_noise.SetFractalGain(domain_warp_fractal_gain);
@@ -241,23 +259,7 @@ real_t FastNoiseLite::get_domain_warp_frequency() const {
void FastNoiseLite::set_domain_warp_fractal_type(DomainWarpFractalType p_domain_warp_fractal_type) {
domain_warp_fractal_type = p_domain_warp_fractal_type;
- // This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices.
- _FastNoiseLite::FractalType type;
- switch (p_domain_warp_fractal_type) {
- case DOMAIN_WARP_FRACTAL_NONE:
- type = _FastNoiseLite::FractalType_None;
- break;
- case DOMAIN_WARP_FRACTAL_PROGRESSIVE:
- type = _FastNoiseLite::FractalType_DomainWarpProgressive;
- break;
- case DOMAIN_WARP_FRACTAL_INDEPENDENT:
- type = _FastNoiseLite::FractalType_DomainWarpIndependent;
- break;
- default:
- type = _FastNoiseLite::FractalType_None;
- }
-
- _domain_warp_noise.SetFractalType(type);
+ _domain_warp_noise.SetFractalType(_convert_domain_warp_fractal_type_enum(p_domain_warp_fractal_type));
emit_changed();
}
@@ -298,6 +300,12 @@ real_t FastNoiseLite::get_domain_warp_fractal_gain() const {
// Noise interface functions.
real_t FastNoiseLite::get_noise_1d(real_t p_x) const {
+ p_x += offset.x;
+ if (domain_warp_enabled) {
+ // Needed since DomainWarp expects a reference.
+ real_t y_dummy = 0;
+ _domain_warp_noise.DomainWarp(p_x, y_dummy);
+ }
return get_noise_2d(p_x, 0.0);
}
@@ -306,10 +314,12 @@ real_t FastNoiseLite::get_noise_2dv(Vector2 p_v) const {
}
real_t FastNoiseLite::get_noise_2d(real_t p_x, real_t p_y) const {
+ p_x += offset.x;
+ p_y += offset.y;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y);
}
- return _noise.GetNoise(p_x + offset.x, p_y + offset.y);
+ return _noise.GetNoise(p_x, p_y);
}
real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const {
@@ -317,10 +327,13 @@ real_t FastNoiseLite::get_noise_3dv(Vector3 p_v) const {
}
real_t FastNoiseLite::get_noise_3d(real_t p_x, real_t p_y, real_t p_z) const {
+ p_x += offset.x;
+ p_y += offset.y;
+ p_z += offset.z;
if (domain_warp_enabled) {
_domain_warp_noise.DomainWarp(p_x, p_y, p_z);
}
- return _noise.GetNoise(p_x + offset.x, p_y + offset.y, p_z + offset.z);
+ return _noise.GetNoise(p_x, p_y, p_z);
}
void FastNoiseLite::_changed() {
diff --git a/modules/noise/fastnoise_lite.h b/modules/noise/fastnoise_lite.h
index 0a4251868b..fe8cd7ce6e 100644
--- a/modules/noise/fastnoise_lite.h
+++ b/modules/noise/fastnoise_lite.h
@@ -127,6 +127,9 @@ private:
real_t domain_warp_fractal_lacunarity = 6;
real_t domain_warp_fractal_gain = 0.5;
+ // This needs manual conversion because Godots Inspector property API does not support discontiguous enum indices.
+ _FastNoiseLite::FractalType _convert_domain_warp_fractal_type_enum(DomainWarpFractalType p_domain_warp_fractal_type);
+
public:
FastNoiseLite();
~FastNoiseLite();
diff --git a/modules/noise/noise_texture.cpp b/modules/noise/noise_texture.cpp
index e9d16e548c..ca55d3b96d 100644
--- a/modules/noise/noise_texture.cpp
+++ b/modules/noise/noise_texture.cpp
@@ -81,8 +81,8 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater,suffix:px"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "in_3d_space"), "set_in_3d_space", "is_in_3d_space");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "generate_mipmaps"), "set_generate_mipmaps", "is_generating_mipmaps");
diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub
index ff320236a7..8783e061d2 100644
--- a/modules/openxr/SCsub
+++ b/modules/openxr/SCsub
@@ -81,6 +81,8 @@ if env["platform"] == "android":
if env["vulkan"]:
env_openxr.add_source_files(module_obj, "extensions/openxr_vulkan_extension.cpp")
+env_openxr.add_source_files(module_obj, "extensions/openxr_htc_vive_tracker_extension.cpp")
+
env.modules_sources += module_obj
if env["tools"]:
diff --git a/modules/openxr/action_map/openxr_action_map.cpp b/modules/openxr/action_map/openxr_action_map.cpp
index 366e131369..0eb5302442 100644
--- a/modules/openxr/action_map/openxr_action_map.cpp
+++ b/modules/openxr/action_map/openxr_action_map.cpp
@@ -182,10 +182,40 @@ void OpenXRActionMap::create_default_action_sets() {
Ref<OpenXRAction> ax_touch = action_set->add_new_action("ax_touch", "A/X touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_button = action_set->add_new_action("by_button", "B/Y button", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> by_touch = action_set->add_new_action("by_touch", "B/Y touching", OpenXRAction::OPENXR_ACTION_BOOL, "/user/hand/left,/user/hand/right");
- Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
+ Ref<OpenXRAction> default_pose = action_set->add_new_action("default_pose", "Default pose", OpenXRAction::OPENXR_ACTION_POSE,
+ "/user/hand/left,"
+ "/user/hand/right,"
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot,"
+ "/user/vive_tracker_htcx/role/right_foot,"
+ "/user/vive_tracker_htcx/role/left_shoulder,"
+ "/user/vive_tracker_htcx/role/right_shoulder,"
+ "/user/vive_tracker_htcx/role/left_elbow,"
+ "/user/vive_tracker_htcx/role/right_elbow,"
+ "/user/vive_tracker_htcx/role/left_knee,"
+ "/user/vive_tracker_htcx/role/right_knee,"
+ "/user/vive_tracker_htcx/role/waist,"
+ "/user/vive_tracker_htcx/role/chest,"
+ "/user/vive_tracker_htcx/role/camera,"
+ "/user/vive_tracker_htcx/role/keyboard");
Ref<OpenXRAction> aim_pose = action_set->add_new_action("aim_pose", "Aim pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
Ref<OpenXRAction> grip_pose = action_set->add_new_action("grip_pose", "Grip pose", OpenXRAction::OPENXR_ACTION_POSE, "/user/hand/left,/user/hand/right");
- Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC, "/user/hand/left,/user/hand/right");
+ Ref<OpenXRAction> haptic = action_set->add_new_action("haptic", "Haptic", OpenXRAction::OPENXR_ACTION_HAPTIC,
+ "/user/hand/left,"
+ "/user/hand/right,"
+ // "/user/vive_tracker_htcx/role/handheld_object," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot,"
+ "/user/vive_tracker_htcx/role/right_foot,"
+ "/user/vive_tracker_htcx/role/left_shoulder,"
+ "/user/vive_tracker_htcx/role/right_shoulder,"
+ "/user/vive_tracker_htcx/role/left_elbow,"
+ "/user/vive_tracker_htcx/role/right_elbow,"
+ "/user/vive_tracker_htcx/role/left_knee,"
+ "/user/vive_tracker_htcx/role/right_knee,"
+ "/user/vive_tracker_htcx/role/waist,"
+ "/user/vive_tracker_htcx/role/chest,"
+ "/user/vive_tracker_htcx/role/camera,"
+ "/user/vive_tracker_htcx/role/keyboard");
// Create our interaction profiles
Ref<OpenXRInteractionProfile> profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/khr/simple_controller");
@@ -399,6 +429,37 @@ void OpenXRActionMap::create_default_action_sets() {
profile->add_new_binding(primary_click, "/user/hand/left/input/trackpad/click,/user/hand/right/input/trackpad/click");
profile->add_new_binding(primary_touch, "/user/hand/left/input/trackpad/touch,/user/hand/right/input/trackpad/touch");
profile->add_new_binding(haptic, "/user/hand/left/output/haptic,/user/hand/right/output/haptic");
+
+ // Create our HTC Vive tracker profile
+ profile = OpenXRInteractionProfile::new_profile("/interaction_profiles/htc/vive_tracker_htcx");
+ profile->add_new_binding(default_pose,
+ // "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_foot/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/left_knee/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/right_knee/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/waist/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/chest/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/camera/input/grip/pose,"
+ "/user/vive_tracker_htcx/role/keyboard/input/grip/pose");
+ profile->add_new_binding(haptic,
+ // "/user/vive_tracker_htcx/role/handheld_object/output/haptic," <-- getting errors on this one
+ "/user/vive_tracker_htcx/role/left_foot/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_foot/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_shoulder/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_shoulder/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_elbow/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_elbow/output/haptic,"
+ "/user/vive_tracker_htcx/role/left_knee/output/haptic,"
+ "/user/vive_tracker_htcx/role/right_knee/output/haptic,"
+ "/user/vive_tracker_htcx/role/waist/output/haptic,"
+ "/user/vive_tracker_htcx/role/chest/output/haptic,"
+ "/user/vive_tracker_htcx/role/camera/output/haptic,"
+ "/user/vive_tracker_htcx/role/keyboard/output/haptic");
add_interaction_profile(profile);
}
diff --git a/modules/openxr/action_map/openxr_defs.cpp b/modules/openxr/action_map/openxr_defs.cpp
index e10326449c..89860199be 100644
--- a/modules/openxr/action_map/openxr_defs.cpp
+++ b/modules/openxr/action_map/openxr_defs.cpp
@@ -32,8 +32,28 @@
// Our top level paths to which devices can be bound
OpenXRDefs::TopLevelPath OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_TOP_LEVEL_PATH_MAX] = {
+ // Core OpenXR paths
{ "Left hand controller", "/user/hand/left" },
{ "Right hand controller", "/user/hand/right" },
+ { "Head", "/user/head" },
+ { "Gamepad", "/user/gamepad" },
+ { "Treadmill", "/user/treadmill" },
+
+ // Specific to HTC tracker extension
+ // { "Handheld object tracker", "/user/vive_tracker_htcx/role/handheld_object" },
+ { "Left foot tracker", "/user/vive_tracker_htcx/role/left_foot" },
+ { "Right foot tracker", "/user/vive_tracker_htcx/role/right_foot" },
+ { "Left shoulder tracker", "/user/vive_tracker_htcx/role/left_shoulder" },
+ { "Right shoulder tracker", "/user/vive_tracker_htcx/role/right_shoulder" },
+ { "Left elbow tracker", "/user/vive_tracker_htcx/role/left_elbow" },
+ { "Right elbow tracker", "/user/vive_tracker_htcx/role/right_elbow" },
+ { "Left knee tracker", "/user/vive_tracker_htcx/role/left_knee" },
+ { "Right knee tracker", "/user/vive_tracker_htcx/role/right_knee" },
+ { "Waist tracker", "/user/vive_tracker_htcx/role/waist" },
+ { "Chest tracker", "/user/vive_tracker_htcx/role/chest" },
+ { "Camera tracker", "/user/vive_tracker_htcx/role/camera" },
+ { "Keyboard tracker", "/user/vive_tracker_htcx/role/keyboard" },
+
};
// Fallback Khronos simple controller
@@ -378,6 +398,137 @@ OpenXRDefs::IOPath OpenXRDefs::huawei_controller_paths[] = {
{ "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_RIGHT_HAND], "/user/hand/right/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
};
+// HTC Vive tracker
+// Interestingly enough trackers don't have buttons or inputs, yet these are defined in the spec.
+// I think this can be supported through attachments on the trackers.
+OpenXRDefs::IOPath OpenXRDefs::vive_tracker_controller_paths[] = {
+ // { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Menu click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/menu/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+ { "Trigger", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trigger/value", OpenXRAction::OPENXR_ACTION_FLOAT },
+
+ // { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trigger click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trigger/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Squeeze click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/squeeze/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+ { "Trackpad", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad", OpenXRAction::OPENXR_ACTION_VECTOR2 },
+
+ // { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad click", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad/click", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+ { "Trackpad touch", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/trackpad/touch", OpenXRAction::OPENXR_ACTION_BOOL },
+
+ // { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+ { "Grip pose", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/input/grip/pose", OpenXRAction::OPENXR_ACTION_POSE },
+
+ // { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_HANDHELD_TRACKER], "/user/vive_tracker_htcx/role/handheld_object/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/left_foot/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_FOOT_TRACKER], "/user/vive_tracker_htcx/role/right_foot/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/left_shoulder/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_SHOULDER_TRACKER], "/user/vive_tracker_htcx/role/right_shoulder/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/left_elbow/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_ELBOW_TRACKER], "/user/vive_tracker_htcx/role/right_elbow/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_LEFT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/left_knee/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_RIGHT_KNEE_TRACKER], "/user/vive_tracker_htcx/role/right_knee/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_WAIST_TRACKER], "/user/vive_tracker_htcx/role/waist/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CHEST_TRACKER], "/user/vive_tracker_htcx/role/chest/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_CAMERA_TRACKER], "/user/vive_tracker_htcx/role/camera/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+ { "Haptic output", &OpenXRDefs::available_top_level_paths[OpenXRDefs::OPENXR_HTC_KEYBOARD_TRACKER], "/user/vive_tracker_htcx/role/keyboard/output/haptic", OpenXRAction::OPENXR_ACTION_HAPTIC },
+};
+
OpenXRDefs::InteractionProfile OpenXRDefs::available_interaction_profiles[] = {
{
"Simple controller", // display_name
@@ -439,6 +590,13 @@ OpenXRDefs::InteractionProfile OpenXRDefs::available_interaction_profiles[] = {
huawei_controller_paths, // io_paths
sizeof(huawei_controller_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
},
+
+ {
+ "HTC Vive tracker", // display_name
+ "/interaction_profiles/htc/vive_tracker_htcx", // openxr_path
+ vive_tracker_controller_paths, // io_paths
+ sizeof(vive_tracker_controller_paths) / sizeof(OpenXRDefs::IOPath) // io_path_count
+ },
};
int OpenXRDefs::available_interaction_profile_count = sizeof(OpenXRDefs::available_interaction_profiles) / sizeof(OpenXRDefs::InteractionProfile);
diff --git a/modules/openxr/action_map/openxr_defs.h b/modules/openxr/action_map/openxr_defs.h
index dbda4757f1..9bdd9a6ded 100644
--- a/modules/openxr/action_map/openxr_defs.h
+++ b/modules/openxr/action_map/openxr_defs.h
@@ -51,8 +51,28 @@
class OpenXRDefs {
public:
enum TOP_LEVEL_PATH {
+ // Core OpenXR toplevel paths
OPENXR_LEFT_HAND,
OPENXR_RIGHT_HAND,
+ OPENXR_HEAD,
+ OPENXR_GAMEPAD,
+ OPENXR_TREADMILL,
+
+ // HTC tracker extension toplevel paths
+ // OPENXR_HTC_HANDHELD_TRACKER,
+ OPENXR_HTC_LEFT_FOOT_TRACKER,
+ OPENXR_HTC_RIGHT_FOOT_TRACKER,
+ OPENXR_HTC_LEFT_SHOULDER_TRACKER,
+ OPENXR_HTC_RIGHT_SHOULDER_TRACKER,
+ OPENXR_HTC_LEFT_ELBOW_TRACKER,
+ OPENXR_HTC_RIGHT_ELBOW_TRACKER,
+ OPENXR_HTC_LEFT_KNEE_TRACKER,
+ OPENXR_HTC_RIGHT_KNEE_TRACKER,
+ OPENXR_HTC_WAIST_TRACKER,
+ OPENXR_HTC_CHEST_TRACKER,
+ OPENXR_HTC_CAMERA_TRACKER,
+ OPENXR_HTC_KEYBOARD_TRACKER,
+
OPENXR_TOP_LEVEL_PATH_MAX
};
@@ -89,6 +109,7 @@ private:
static IOPath vive_cosmos_paths[];
static IOPath vive_focus3_paths[];
static IOPath huawei_controller_paths[];
+ static IOPath vive_tracker_controller_paths[];
static InteractionProfile available_interaction_profiles[];
static int available_interaction_profile_count;
diff --git a/modules/openxr/editor/openxr_action_editor.cpp b/modules/openxr/editor/openxr_action_editor.cpp
index e2a4f67f16..41c6465f43 100644
--- a/modules/openxr/editor/openxr_action_editor.cpp
+++ b/modules/openxr/editor/openxr_action_editor.cpp
@@ -63,8 +63,7 @@ void OpenXRActionEditor::_on_action_localized_name_changed(const String p_new_te
}
void OpenXRActionEditor::_on_item_selected(int p_idx) {
- ERR_FAIL_COND(p_idx < 0);
- ERR_FAIL_COND(p_idx >= OpenXRAction::OPENXR_ACTION_MAX);
+ ERR_FAIL_INDEX(p_idx, OpenXRAction::OPENXR_ACTION_MAX);
action->set_action_type(OpenXRAction::ActionType(p_idx));
}
diff --git a/modules/openxr/editor/openxr_action_map_editor.cpp b/modules/openxr/editor/openxr_action_map_editor.cpp
index 6e9a2e1b61..87b7f50224 100644
--- a/modules/openxr/editor/openxr_action_map_editor.cpp
+++ b/modules/openxr/editor/openxr_action_map_editor.cpp
@@ -344,6 +344,7 @@ OpenXRActionMapEditor::OpenXRActionMapEditor() {
tabs = memnew(TabContainer);
tabs->set_h_size_flags(SIZE_EXPAND_FILL);
tabs->set_v_size_flags(SIZE_EXPAND_FILL);
+ tabs->set_theme_type_variation("TabContainerOdd");
tabs->connect("tab_changed", callable_mp(this, &OpenXRActionMapEditor::_on_tabs_tab_changed));
tabs->connect("tab_button_pressed", callable_mp(this, &OpenXRActionMapEditor::_on_tab_button_pressed));
add_child(tabs);
diff --git a/modules/openxr/extensions/openxr_extension_wrapper.h b/modules/openxr/extensions/openxr_extension_wrapper.h
index f3064041b8..0f7c0ba0bc 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper.h
@@ -33,7 +33,7 @@
#include "core/error/error_macros.h"
#include "core/math/camera_matrix.h"
-#include "core/templates/map.h"
+#include "core/templates/hash_map.h"
#include "core/templates/rid.h"
#include "thirdparty/openxr/src/common/xr_linear.h"
@@ -49,10 +49,10 @@ protected:
// Store extension we require.
// If bool pointer is a nullptr this means this extension is mandatory and initialisation will fail if it is not available
// If bool pointer is set, value will be set to true or false depending on whether extension is available
- Map<const char *, bool *> request_extensions;
+ HashMap<String, bool *> request_extensions;
public:
- virtual Map<const char *, bool *> get_request_extensions() {
+ virtual HashMap<String, bool *> get_request_extensions() {
return request_extensions;
}
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
new file mode 100644
index 0000000000..302acf4e30
--- /dev/null
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.cpp
@@ -0,0 +1,67 @@
+/*************************************************************************/
+/* openxr_htc_vive_tracker_extension.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "openxr_htc_vive_tracker_extension.h"
+#include "core/string/print_string.h"
+
+OpenXRHTCViveTrackerExtension *OpenXRHTCViveTrackerExtension::singleton = nullptr;
+
+OpenXRHTCViveTrackerExtension *OpenXRHTCViveTrackerExtension::get_singleton() {
+ return singleton;
+}
+
+OpenXRHTCViveTrackerExtension::OpenXRHTCViveTrackerExtension(OpenXRAPI *p_openxr_api) :
+ OpenXRExtensionWrapper(p_openxr_api) {
+ singleton = this;
+
+ request_extensions[XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME] = &available;
+}
+
+OpenXRHTCViveTrackerExtension::~OpenXRHTCViveTrackerExtension() {
+ singleton = nullptr;
+}
+
+bool OpenXRHTCViveTrackerExtension::is_available() {
+ return available;
+}
+
+bool OpenXRHTCViveTrackerExtension::on_event_polled(const XrEventDataBuffer &event) {
+ switch (event.type) {
+ case XR_TYPE_EVENT_DATA_VIVE_TRACKER_CONNECTED_HTCX: {
+ // Investigate if we need to do more here
+ print_verbose("OpenXR EVENT: VIVE tracker connected");
+
+ return true;
+ } break;
+ default: {
+ return false;
+ } break;
+ }
+}
diff --git a/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
new file mode 100644
index 0000000000..7670bc074b
--- /dev/null
+++ b/modules/openxr/extensions/openxr_htc_vive_tracker_extension.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* openxr_htc_vive_tracker_extension.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
+#define OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRHTCViveTrackerExtension : public OpenXRExtensionWrapper {
+public:
+ static OpenXRHTCViveTrackerExtension *get_singleton();
+
+ OpenXRHTCViveTrackerExtension(OpenXRAPI *p_openxr_api);
+ virtual ~OpenXRHTCViveTrackerExtension() override;
+
+ bool is_available();
+ virtual bool on_event_polled(const XrEventDataBuffer &event) override;
+
+private:
+ static OpenXRHTCViveTrackerExtension *singleton;
+
+ bool available = false;
+};
+
+#endif // !OPENXR_HTC_VIVE_TRACKER_EXTENSION_H
diff --git a/modules/openxr/extensions/openxr_vulkan_extension.cpp b/modules/openxr/extensions/openxr_vulkan_extension.cpp
index 2ddf3b8a7d..3d3d4de5b6 100644
--- a/modules/openxr/extensions/openxr_vulkan_extension.cpp
+++ b/modules/openxr/extensions/openxr_vulkan_extension.cpp
@@ -34,7 +34,6 @@
#include "../openxr_api.h"
#include "../openxr_util.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
-#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"
#include "servers/rendering_server.h"
@@ -439,7 +438,6 @@ bool OpenXRVulkanExtension::copy_render_target_to_image(RID p_from_render_target
SwapchainGraphicsData *data = (SwapchainGraphicsData *)p_swapchain_graphics_data;
ERR_FAIL_NULL_V(data, false);
ERR_FAIL_COND_V(p_from_render_target.is_null(), false);
- ERR_FAIL_NULL_V(RendererStorageRD::base_singleton, false);
RID source_image = RendererRD::TextureStorage::get_singleton()->render_target_get_rd_texture(p_from_render_target);
ERR_FAIL_COND_V(source_image.is_null(), false);
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index d0f67b7f5d..5e35942012 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -48,7 +48,9 @@
#include "extensions/openxr_vulkan_extension.h"
#endif
-#include "openxr_interface.h"
+#include "extensions/openxr_htc_vive_tracker_extension.h"
+
+#include "modules/openxr/openxr_interface.h"
OpenXRAPI *OpenXRAPI::singleton = nullptr;
@@ -170,9 +172,9 @@ bool OpenXRAPI::load_supported_extensions() {
return true;
}
-bool OpenXRAPI::is_extension_supported(const char *p_extension) const {
+bool OpenXRAPI::is_extension_supported(const String &p_extension) const {
for (uint32_t i = 0; i < num_supported_extensions; i++) {
- if (strcmp(supported_extensions[i].extensionName, p_extension) == 0) {
+ if (supported_extensions[i].extensionName == p_extension) {
#ifdef DEBUG
print_line("OpenXR: requested extension", p_extension, "is supported");
#endif
@@ -204,9 +206,9 @@ bool OpenXRAPI::create_instance() {
// Create our OpenXR instance, this will query any registered extension wrappers for extensions we need to enable.
// Append the extensions requested by the registered extension wrappers.
- Map<const char *, bool *> requested_extensions;
+ HashMap<String, bool *> requested_extensions;
for (OpenXRExtensionWrapper *wrapper : registered_extension_wrappers) {
- Map<const char *, bool *> wrapper_request_extensions = wrapper->get_request_extensions();
+ const HashMap<String, bool *> &wrapper_request_extensions = wrapper->get_request_extensions();
// requested_extensions.insert(wrapper_request_extensions.begin(), wrapper_request_extensions.end());
for (auto &requested_extension : wrapper_request_extensions) {
@@ -224,6 +226,7 @@ bool OpenXRAPI::create_instance() {
// Check which extensions are supported
enabled_extensions.clear();
+
for (auto &requested_extension : requested_extensions) {
if (!is_extension_supported(requested_extension.key)) {
if (requested_extension.value == nullptr) {
@@ -238,13 +241,18 @@ bool OpenXRAPI::create_instance() {
*requested_extension.value = true;
// and record that we want to enable it
- enabled_extensions.push_back(requested_extension.key);
+ enabled_extensions.push_back(requested_extension.key.ascii());
} else {
// record that we want to enable this
- enabled_extensions.push_back(requested_extension.key);
+ enabled_extensions.push_back(requested_extension.key.ascii());
}
}
+ Vector<const char *> extension_ptrs;
+ for (int i = 0; i < enabled_extensions.size(); i++) {
+ extension_ptrs.push_back(enabled_extensions[i].get_data());
+ }
+
// Get our project name
String project_name = GLOBAL_GET("application/config/name");
@@ -264,8 +272,8 @@ bool OpenXRAPI::create_instance() {
application_info, // applicationInfo
0, // enabledApiLayerCount, need to find out if we need support for this?
nullptr, // enabledApiLayerNames
- uint32_t(enabled_extensions.size()), // enabledExtensionCount
- enabled_extensions.ptr() // enabledExtensionNames
+ uint32_t(extension_ptrs.size()), // enabledExtensionCount
+ extension_ptrs.ptr() // enabledExtensionNames
};
copy_string_to_char_buffer(project_name, instance_create_info.applicationInfo.applicationName, XR_MAX_APPLICATION_NAME_SIZE);
@@ -1096,7 +1104,7 @@ Size2 OpenXRAPI::get_recommended_target_size() {
return target_size;
}
-XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
XrResult result;
ERR_FAIL_COND_V(!running, XRPose::XR_TRACKING_CONFIDENCE_NONE);
@@ -1638,6 +1646,9 @@ OpenXRAPI::OpenXRAPI() {
// our android wrapper will initialize our android loader at this point
register_extension_wrapper(memnew(OpenXRAndroidExtension(this)));
#endif
+
+ // register our other extensions
+ register_extension_wrapper(memnew(OpenXRHTCViveTrackerExtension(this)));
}
OpenXRAPI::~OpenXRAPI() {
@@ -1724,7 +1735,7 @@ XRPose::TrackingConfidence OpenXRAPI::transform_from_location(const XrHandJointL
return _transform_from_location(p_location, r_transform);
}
-void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity) {
+void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
if (p_velocity.velocityFlags & XR_SPACE_VELOCITY_LINEAR_VALID_BIT) {
XrVector3f linear_velocity = p_velocity.linearVelocity;
r_linear_velocity = Vector3(linear_velocity.x, linear_velocity.y, linear_velocity.z);
@@ -2297,7 +2308,7 @@ Vector2 OpenXRAPI::get_action_vector2(RID p_action, RID p_tracker) {
return result_state.isActive ? Vector2(result_state.currentState.x, result_state.currentState.y) : Vector2();
}
-XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity) {
+XRPose::TrackingConfidence OpenXRAPI::get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity) {
ERR_FAIL_COND_V(session == XR_NULL_HANDLE, XRPose::XR_TRACKING_CONFIDENCE_NONE);
Action *action = action_owner.get_or_null(p_action);
ERR_FAIL_NULL_V(action, XRPose::XR_TRACKING_CONFIDENCE_NONE);
diff --git a/modules/openxr/openxr_api.h b/modules/openxr/openxr_api.h
index 702f6b9b1d..fe9e2937b2 100644
--- a/modules/openxr/openxr_api.h
+++ b/modules/openxr/openxr_api.h
@@ -37,7 +37,7 @@
#include "core/math/vector2.h"
#include "core/os/memory.h"
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/rid_owner.h"
#include "core/templates/vector.h"
#include "servers/xr/xr_pose.h"
@@ -73,7 +73,7 @@ private:
uint32_t num_supported_extensions = 0;
XrExtensionProperties *supported_extensions = nullptr;
Vector<OpenXRExtensionWrapper *> registered_extension_wrappers;
- Vector<const char *> enabled_extensions;
+ Vector<CharString> enabled_extensions;
bool ext_hp_mixed_reality_available = false;
bool ext_samsung_odyssey_available = false;
@@ -132,7 +132,7 @@ private:
bool load_layer_properties();
bool load_supported_extensions();
- bool is_extension_supported(const char *p_extension) const;
+ bool is_extension_supported(const String &p_extension) const;
// instance
bool create_instance();
@@ -225,7 +225,7 @@ protected:
// helper method to get a valid Transform3D from an openxr space location
XRPose::TrackingConfidence transform_from_location(const XrSpaceLocation &p_location, Transform3D &r_transform);
XRPose::TrackingConfidence transform_from_location(const XrHandJointLocationEXT &p_location, Transform3D &r_transform);
- void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 r_angular_velocity);
+ void parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
public:
static bool openxr_is_enabled(bool p_check_run_in_editor = true);
@@ -247,7 +247,7 @@ public:
bool can_render() { return instance != XR_NULL_HANDLE && session != XR_NULL_HANDLE && running && view_pose_valid && frame_state.shouldRender; };
Size2 get_recommended_target_size();
- XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_head_center(Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool get_view_transform(uint32_t p_view, Transform3D &r_transform);
bool get_view_projection(uint32_t p_view, double p_z_near, double p_z_far, CameraMatrix &p_camera_matrix);
bool process();
@@ -285,7 +285,7 @@ public:
bool get_action_bool(RID p_action, RID p_tracker);
float get_action_float(RID p_action, RID p_tracker);
Vector2 get_action_vector2(RID p_action, RID p_tracker);
- XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, const Vector3 &r_angular_velocity);
+ XRPose::TrackingConfidence get_action_pose(RID p_action, RID p_tracker, Transform3D &r_transform, Vector3 &r_linear_velocity, Vector3 &r_angular_velocity);
bool trigger_haptic_pulse(RID p_action, RID p_tracker, float p_frequency, float p_amplitude, XrDuration p_duration_ns);
OpenXRAPI();
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index 41ce8c019e..9dfa005600 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -57,7 +57,24 @@ PackedStringArray OpenXRInterface::get_suggested_tracker_names() const {
PackedStringArray arr = {
"left_hand", // /user/hand/left is mapped to our defaults
"right_hand", // /user/hand/right is mapped to our defaults
- "/user/treadmill"
+ "/user/treadmill",
+
+ // Even though these are only available if you have the tracker extension,
+ // we add these as we may be deploying on a different platform than our
+ // editor is running on.
+ "/user/vive_tracker_htcx/role/handheld_object",
+ "/user/vive_tracker_htcx/role/left_foot",
+ "/user/vive_tracker_htcx/role/right_foot",
+ "/user/vive_tracker_htcx/role/left_shoulder",
+ "/user/vive_tracker_htcx/role/right_shoulder",
+ "/user/vive_tracker_htcx/role/left_elbow",
+ "/user/vive_tracker_htcx/role/right_elbow",
+ "/user/vive_tracker_htcx/role/left_knee",
+ "/user/vive_tracker_htcx/role/right_knee",
+ "/user/vive_tracker_htcx/role/waist",
+ "/user/vive_tracker_htcx/role/chest",
+ "/user/vive_tracker_htcx/role/camera",
+ "/user/vive_tracker_htcx/role/keyboard"
};
return arr;
@@ -113,7 +130,7 @@ void OpenXRInterface::_load_action_map() {
// process our action map
if (action_map.is_valid()) {
- Map<Ref<OpenXRAction>, Action *> xr_actions;
+ HashMap<Ref<OpenXRAction>, Action *> xr_actions;
Array action_sets = action_map->get_action_sets();
for (int i = 0; i < action_sets.size(); i++) {
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
index ecc256bff9..9b35b5616e 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -152,17 +152,17 @@ void LightmapRaycasterEmbree::commit() {
rtcCommitScene(embree_scene);
}
-void LightmapRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
- for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
- rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+void LightmapRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) {
+ for (const int &E : p_mesh_ids) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes = p_mesh_ids;
}
void LightmapRaycasterEmbree::clear_mesh_filter() {
- for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
- rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ for (const int &E : filter_meshes) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes.clear();
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h
index c420e617e5..4266b46ea8 100644
--- a/modules/raycast/lightmap_raycaster.h
+++ b/modules/raycast/lightmap_raycaster.h
@@ -52,8 +52,8 @@ private:
static void filter_function(const struct RTCFilterFunctionNArguments *p_args);
- Map<unsigned int, AlphaTextureData> alpha_textures;
- Set<int> filter_meshes;
+ HashMap<unsigned int, AlphaTextureData> alpha_textures;
+ HashSet<int> filter_meshes;
public:
virtual bool intersect(Ray &p_ray) override;
@@ -64,7 +64,7 @@ public:
virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) override;
virtual void commit() override;
- virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override;
virtual void clear_mesh_filter() override;
static LightmapRaycaster *create_embree_raycaster();
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index f49cd08698..89e75f774e 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -223,9 +223,9 @@ void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3
occluder->vertices = p_vertices;
occluder->indices = p_indices;
- for (Set<InstanceID>::Element *E = occluder->users.front(); E; E = E->next()) {
- RID scenario_rid = E->get().scenario;
- RID instance_rid = E->get().instance;
+ for (const InstanceID &E : occluder->users) {
+ RID scenario_rid = E.scenario;
+ RID instance_rid = E.instance;
ERR_CONTINUE(!scenarios.has(scenario_rid));
Scenario &scenario = scenarios[scenario_rid];
ERR_CONTINUE(!scenario.instances.has(instance_rid));
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index 8453c5341d..6562c4e9c4 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -86,11 +86,13 @@ private:
RID scenario;
RID instance;
- bool operator<(const InstanceID &rhs) const {
- if (instance == rhs.instance) {
- return rhs.scenario < scenario;
- }
- return instance < rhs.instance;
+ static uint32_t hash(const InstanceID &p_ins) {
+ uint32_t h = hash_murmur3_one_64(p_ins.scenario.get_id());
+ return hash_fmix32(hash_murmur3_one_64(p_ins.instance.get_id(), h));
+ }
+ bool operator==(const InstanceID &rhs) const {
+ return instance == rhs.instance && rhs.scenario == scenario;
+ ;
}
InstanceID() {}
@@ -101,7 +103,7 @@ private:
struct Occluder {
PackedVector3Array vertices;
PackedInt32Array indices;
- Set<InstanceID> users;
+ HashSet<InstanceID, InstanceID> users;
};
struct OccluderInstance {
@@ -136,7 +138,7 @@ private:
int current_scene_idx = 0;
HashMap<RID, OccluderInstance> instances;
- Set<RID> dirty_instances; // To avoid duplicates
+ HashSet<RID> dirty_instances; // To avoid duplicates
LocalVector<RID> dirty_instances_array; // To iterate and split into threads
LocalVector<RID> removed_instances;
diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster.cpp
index 9df6d5ff43..7659eea27f 100644
--- a/modules/raycast/static_raycaster.cpp
+++ b/modules/raycast/static_raycaster.cpp
@@ -94,17 +94,17 @@ void StaticRaycasterEmbree::commit() {
rtcCommitScene(embree_scene);
}
-void StaticRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
- for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
- rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+void StaticRaycasterEmbree::set_mesh_filter(const HashSet<int> &p_mesh_ids) {
+ for (const int &E : p_mesh_ids) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes = p_mesh_ids;
}
void StaticRaycasterEmbree::clear_mesh_filter() {
- for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
- rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ for (const int &E : filter_meshes) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E));
}
rtcCommitScene(embree_scene);
filter_meshes.clear();
diff --git a/modules/raycast/static_raycaster.h b/modules/raycast/static_raycaster.h
index cce19ba60d..e2909f9b56 100644
--- a/modules/raycast/static_raycaster.h
+++ b/modules/raycast/static_raycaster.h
@@ -41,7 +41,7 @@ private:
static RTCDevice embree_device;
RTCScene embree_scene;
- Set<int> filter_meshes;
+ HashSet<int> filter_meshes;
public:
virtual bool intersect(Ray &p_ray) override;
@@ -50,7 +50,7 @@ public:
virtual void add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) override;
virtual void commit() override;
- virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void set_mesh_filter(const HashSet<int> &p_mesh_ids) override;
virtual void clear_mesh_filter() override;
static StaticRaycaster *create_embree_raycaster();
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index ee1137b71f..bbe92139e0 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -52,9 +52,9 @@ int RegExMatch::_find(const Variant &p_name) const {
return i;
} else if (p_name.get_type() == Variant::STRING) {
- const Map<String, int>::Element *found = names.find((String)p_name);
+ HashMap<String, int>::ConstIterator found = names.find((String)p_name);
if (found) {
- return found->value();
+ return found->value;
}
}
@@ -75,8 +75,8 @@ int RegExMatch::get_group_count() const {
Dictionary RegExMatch::get_names() const {
Dictionary result;
- for (const Map<String, int>::Element *i = names.front(); i != nullptr; i = i->next()) {
- result[i->key()] = i->value();
+ for (const KeyValue<String, int> &E : names) {
+ result[E.key] = E.value;
}
return result;
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
index e7221f4070..1455188670 100644
--- a/modules/regex/regex.h
+++ b/modules/regex/regex.h
@@ -33,7 +33,7 @@
#include "core/object/ref_counted.h"
#include "core/string/ustring.h"
-#include "core/templates/map.h"
+#include "core/templates/rb_map.h"
#include "core/templates/vector.h"
#include "core/variant/array.h"
#include "core/variant/dictionary.h"
@@ -48,7 +48,7 @@ class RegExMatch : public RefCounted {
String subject;
Vector<Range> data;
- Map<String, int> names;
+ HashMap<String, int> names;
friend class RegEx;
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 525d4d3efb..2d764a4006 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -75,18 +75,18 @@ if env["builtin_harfbuzz"]:
"src/hb-ot-meta.cc",
"src/hb-ot-metrics.cc",
"src/hb-ot-name.cc",
- "src/hb-ot-shape-complex-arabic.cc",
- "src/hb-ot-shape-complex-default.cc",
- "src/hb-ot-shape-complex-hangul.cc",
- "src/hb-ot-shape-complex-hebrew.cc",
- "src/hb-ot-shape-complex-indic-table.cc",
- "src/hb-ot-shape-complex-indic.cc",
- "src/hb-ot-shape-complex-khmer.cc",
- "src/hb-ot-shape-complex-myanmar.cc",
- "src/hb-ot-shape-complex-syllabic.cc",
- "src/hb-ot-shape-complex-thai.cc",
- "src/hb-ot-shape-complex-use.cc",
- "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shaper-arabic.cc",
+ "src/hb-ot-shaper-default.cc",
+ "src/hb-ot-shaper-hangul.cc",
+ "src/hb-ot-shaper-hebrew.cc",
+ "src/hb-ot-shaper-indic-table.cc",
+ "src/hb-ot-shaper-indic.cc",
+ "src/hb-ot-shaper-khmer.cc",
+ "src/hb-ot-shaper-myanmar.cc",
+ "src/hb-ot-shaper-syllabic.cc",
+ "src/hb-ot-shaper-thai.cc",
+ "src/hb-ot-shaper-use.cc",
+ "src/hb-ot-shaper-vowel-constraints.cc",
"src/hb-ot-shape-fallback.cc",
"src/hb-ot-shape-normalize.cc",
"src/hb-ot-shape.cc",
@@ -442,7 +442,7 @@ if env["builtin_icu"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- icu_data_name = "icudt70l.dat"
+ icu_data_name = "icudt71l.dat"
if env_icu["tools"]:
env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name)
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index 1c38398c88..69848a9e52 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -116,6 +116,7 @@ if env["freetype_enabled"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -164,7 +165,14 @@ if env["freetype_enabled"]:
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)])
+ env_freetype.Append(
+ CPPDEFINES=[
+ "FT2_BUILD_LIBRARY",
+ "FT_CONFIG_OPTION_USE_PNG",
+ ("PNG_ARM_NEON_OPT", 0),
+ "FT_CONFIG_OPTION_SYSTEM_ZLIB",
+ ]
+ )
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
@@ -212,18 +220,18 @@ thirdparty_harfbuzz_sources = [
"src/hb-ot-meta.cc",
"src/hb-ot-metrics.cc",
"src/hb-ot-name.cc",
- "src/hb-ot-shape-complex-arabic.cc",
- "src/hb-ot-shape-complex-default.cc",
- "src/hb-ot-shape-complex-hangul.cc",
- "src/hb-ot-shape-complex-hebrew.cc",
- "src/hb-ot-shape-complex-indic-table.cc",
- "src/hb-ot-shape-complex-indic.cc",
- "src/hb-ot-shape-complex-khmer.cc",
- "src/hb-ot-shape-complex-myanmar.cc",
- "src/hb-ot-shape-complex-syllabic.cc",
- "src/hb-ot-shape-complex-thai.cc",
- "src/hb-ot-shape-complex-use.cc",
- "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shaper-arabic.cc",
+ "src/hb-ot-shaper-default.cc",
+ "src/hb-ot-shaper-hangul.cc",
+ "src/hb-ot-shaper-hebrew.cc",
+ "src/hb-ot-shaper-indic-table.cc",
+ "src/hb-ot-shaper-indic.cc",
+ "src/hb-ot-shaper-khmer.cc",
+ "src/hb-ot-shaper-myanmar.cc",
+ "src/hb-ot-shaper-syllabic.cc",
+ "src/hb-ot-shaper-thai.cc",
+ "src/hb-ot-shaper-use.cc",
+ "src/hb-ot-shaper-vowel-constraints.cc",
"src/hb-ot-shape-fallback.cc",
"src/hb-ot-shape-normalize.cc",
"src/hb-ot-shape.cc",
@@ -564,7 +572,7 @@ thirdparty_icu_sources = [
]
thirdparty_icu_sources = [thirdparty_icu_dir + file for file in thirdparty_icu_sources]
-icu_data_name = "icudt70l.dat"
+icu_data_name = "icudt71l.dat"
if env["static_icu_data"]:
env_icu.Depends("../../../thirdparty/icu4c/icudata.gen.h", "../../../thirdparty/icu4c/" + icu_data_name)
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index fe3a43dc97..fe2279df69 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -75,7 +75,7 @@ using namespace core_bind;
hb_font_funcs_t *TextServerAdvanced::funcs = nullptr;
-TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref) {
bmp_font_t *bm_font = memnew(bmp_font_t);
if (!bm_font) {
@@ -228,11 +228,11 @@ void TextServerAdvanced::_bmp_free_font_funcs() {
}
}
-void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref) {
hb_font_set_funcs(p_font, funcs, _bmp_font_create(p_face, p_unref), _bmp_font_destroy);
}
-hb_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
+hb_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
hb_font_t *font;
hb_face_t *face = hb_face_create(nullptr, 0);
@@ -375,7 +375,7 @@ int64_t TextServerAdvanced::get_features() const {
void TextServerAdvanced::free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
@@ -476,259 +476,267 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const {
}
}
-_FORCE_INLINE_ void TextServerAdvanced::_insert_feature(const StringName &p_name, int32_t p_tag) {
+_FORCE_INLINE_ void TextServerAdvanced::_insert_feature(const StringName &p_name, int32_t p_tag, Variant::Type p_vtype, bool p_hidden) {
+ FeatureInfo fi;
+ fi.name = p_name;
+ fi.vtype = p_vtype;
+ fi.hidden = p_hidden;
+
feature_sets.insert(p_name, p_tag);
- feature_sets_inv.insert(p_tag, p_name);
+ feature_sets_inv.insert(p_tag, fi);
}
void TextServerAdvanced::_insert_feature_sets() {
// Registered OpenType feature tags.
- _insert_feature("access_all_alternates", HB_TAG('a', 'a', 'l', 't'));
- _insert_feature("above_base_forms", HB_TAG('a', 'b', 'v', 'f'));
- _insert_feature("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'));
- _insert_feature("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'));
- _insert_feature("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'));
- _insert_feature("akhands", HB_TAG('a', 'k', 'h', 'n'));
- _insert_feature("below_base_forms", HB_TAG('b', 'l', 'w', 'f'));
- _insert_feature("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'));
- _insert_feature("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'));
- _insert_feature("contextual_alternates", HB_TAG('c', 'a', 'l', 't'));
- _insert_feature("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'));
- _insert_feature("glyph_composition", HB_TAG('c', 'c', 'm', 'p'));
- _insert_feature("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'));
- _insert_feature("conjunct_forms", HB_TAG('c', 'j', 'c', 't'));
- _insert_feature("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'));
- _insert_feature("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'));
- _insert_feature("capital_spacing", HB_TAG('c', 'p', 's', 'p'));
- _insert_feature("contextual_swash", HB_TAG('c', 's', 'w', 'h'));
- _insert_feature("cursive_positioning", HB_TAG('c', 'u', 'r', 's'));
- _insert_feature("character_variant_01", HB_TAG('c', 'v', '0', '1'));
- _insert_feature("character_variant_02", HB_TAG('c', 'v', '0', '2'));
- _insert_feature("character_variant_03", HB_TAG('c', 'v', '0', '3'));
- _insert_feature("character_variant_04", HB_TAG('c', 'v', '0', '4'));
- _insert_feature("character_variant_05", HB_TAG('c', 'v', '0', '5'));
- _insert_feature("character_variant_06", HB_TAG('c', 'v', '0', '6'));
- _insert_feature("character_variant_07", HB_TAG('c', 'v', '0', '7'));
- _insert_feature("character_variant_08", HB_TAG('c', 'v', '0', '8'));
- _insert_feature("character_variant_09", HB_TAG('c', 'v', '0', '9'));
- _insert_feature("character_variant_10", HB_TAG('c', 'v', '1', '0'));
- _insert_feature("character_variant_11", HB_TAG('c', 'v', '1', '1'));
- _insert_feature("character_variant_12", HB_TAG('c', 'v', '1', '2'));
- _insert_feature("character_variant_13", HB_TAG('c', 'v', '1', '3'));
- _insert_feature("character_variant_14", HB_TAG('c', 'v', '1', '4'));
- _insert_feature("character_variant_15", HB_TAG('c', 'v', '1', '5'));
- _insert_feature("character_variant_16", HB_TAG('c', 'v', '1', '6'));
- _insert_feature("character_variant_17", HB_TAG('c', 'v', '1', '7'));
- _insert_feature("character_variant_18", HB_TAG('c', 'v', '1', '8'));
- _insert_feature("character_variant_19", HB_TAG('c', 'v', '1', '9'));
- _insert_feature("character_variant_20", HB_TAG('c', 'v', '2', '0'));
- _insert_feature("character_variant_21", HB_TAG('c', 'v', '2', '1'));
- _insert_feature("character_variant_22", HB_TAG('c', 'v', '2', '2'));
- _insert_feature("character_variant_23", HB_TAG('c', 'v', '2', '3'));
- _insert_feature("character_variant_24", HB_TAG('c', 'v', '2', '4'));
- _insert_feature("character_variant_25", HB_TAG('c', 'v', '2', '5'));
- _insert_feature("character_variant_26", HB_TAG('c', 'v', '2', '6'));
- _insert_feature("character_variant_27", HB_TAG('c', 'v', '2', '7'));
- _insert_feature("character_variant_28", HB_TAG('c', 'v', '2', '8'));
- _insert_feature("character_variant_29", HB_TAG('c', 'v', '2', '9'));
- _insert_feature("character_variant_30", HB_TAG('c', 'v', '3', '0'));
- _insert_feature("character_variant_31", HB_TAG('c', 'v', '3', '1'));
- _insert_feature("character_variant_32", HB_TAG('c', 'v', '3', '2'));
- _insert_feature("character_variant_33", HB_TAG('c', 'v', '3', '3'));
- _insert_feature("character_variant_34", HB_TAG('c', 'v', '3', '4'));
- _insert_feature("character_variant_35", HB_TAG('c', 'v', '3', '5'));
- _insert_feature("character_variant_36", HB_TAG('c', 'v', '3', '6'));
- _insert_feature("character_variant_37", HB_TAG('c', 'v', '3', '7'));
- _insert_feature("character_variant_38", HB_TAG('c', 'v', '3', '8'));
- _insert_feature("character_variant_39", HB_TAG('c', 'v', '3', '9'));
- _insert_feature("character_variant_40", HB_TAG('c', 'v', '4', '0'));
- _insert_feature("character_variant_41", HB_TAG('c', 'v', '4', '1'));
- _insert_feature("character_variant_42", HB_TAG('c', 'v', '4', '2'));
- _insert_feature("character_variant_43", HB_TAG('c', 'v', '4', '3'));
- _insert_feature("character_variant_44", HB_TAG('c', 'v', '4', '4'));
- _insert_feature("character_variant_45", HB_TAG('c', 'v', '4', '5'));
- _insert_feature("character_variant_46", HB_TAG('c', 'v', '4', '6'));
- _insert_feature("character_variant_47", HB_TAG('c', 'v', '4', '7'));
- _insert_feature("character_variant_48", HB_TAG('c', 'v', '4', '8'));
- _insert_feature("character_variant_49", HB_TAG('c', 'v', '4', '9'));
- _insert_feature("character_variant_50", HB_TAG('c', 'v', '5', '0'));
- _insert_feature("character_variant_51", HB_TAG('c', 'v', '5', '1'));
- _insert_feature("character_variant_52", HB_TAG('c', 'v', '5', '2'));
- _insert_feature("character_variant_53", HB_TAG('c', 'v', '5', '3'));
- _insert_feature("character_variant_54", HB_TAG('c', 'v', '5', '4'));
- _insert_feature("character_variant_55", HB_TAG('c', 'v', '5', '5'));
- _insert_feature("character_variant_56", HB_TAG('c', 'v', '5', '6'));
- _insert_feature("character_variant_57", HB_TAG('c', 'v', '5', '7'));
- _insert_feature("character_variant_58", HB_TAG('c', 'v', '5', '8'));
- _insert_feature("character_variant_59", HB_TAG('c', 'v', '5', '9'));
- _insert_feature("character_variant_60", HB_TAG('c', 'v', '6', '0'));
- _insert_feature("character_variant_61", HB_TAG('c', 'v', '6', '1'));
- _insert_feature("character_variant_62", HB_TAG('c', 'v', '6', '2'));
- _insert_feature("character_variant_63", HB_TAG('c', 'v', '6', '3'));
- _insert_feature("character_variant_64", HB_TAG('c', 'v', '6', '4'));
- _insert_feature("character_variant_65", HB_TAG('c', 'v', '6', '5'));
- _insert_feature("character_variant_66", HB_TAG('c', 'v', '6', '6'));
- _insert_feature("character_variant_67", HB_TAG('c', 'v', '6', '7'));
- _insert_feature("character_variant_68", HB_TAG('c', 'v', '6', '8'));
- _insert_feature("character_variant_69", HB_TAG('c', 'v', '6', '9'));
- _insert_feature("character_variant_70", HB_TAG('c', 'v', '7', '0'));
- _insert_feature("character_variant_71", HB_TAG('c', 'v', '7', '1'));
- _insert_feature("character_variant_72", HB_TAG('c', 'v', '7', '2'));
- _insert_feature("character_variant_73", HB_TAG('c', 'v', '7', '3'));
- _insert_feature("character_variant_74", HB_TAG('c', 'v', '7', '4'));
- _insert_feature("character_variant_75", HB_TAG('c', 'v', '7', '5'));
- _insert_feature("character_variant_76", HB_TAG('c', 'v', '7', '6'));
- _insert_feature("character_variant_77", HB_TAG('c', 'v', '7', '7'));
- _insert_feature("character_variant_78", HB_TAG('c', 'v', '7', '8'));
- _insert_feature("character_variant_79", HB_TAG('c', 'v', '7', '9'));
- _insert_feature("character_variant_80", HB_TAG('c', 'v', '8', '0'));
- _insert_feature("character_variant_81", HB_TAG('c', 'v', '8', '1'));
- _insert_feature("character_variant_82", HB_TAG('c', 'v', '8', '2'));
- _insert_feature("character_variant_83", HB_TAG('c', 'v', '8', '3'));
- _insert_feature("character_variant_84", HB_TAG('c', 'v', '8', '4'));
- _insert_feature("character_variant_85", HB_TAG('c', 'v', '8', '5'));
- _insert_feature("character_variant_86", HB_TAG('c', 'v', '8', '6'));
- _insert_feature("character_variant_87", HB_TAG('c', 'v', '8', '7'));
- _insert_feature("character_variant_88", HB_TAG('c', 'v', '8', '8'));
- _insert_feature("character_variant_89", HB_TAG('c', 'v', '8', '9'));
- _insert_feature("character_variant_90", HB_TAG('c', 'v', '9', '0'));
- _insert_feature("character_variant_91", HB_TAG('c', 'v', '9', '1'));
- _insert_feature("character_variant_92", HB_TAG('c', 'v', '9', '2'));
- _insert_feature("character_variant_93", HB_TAG('c', 'v', '9', '3'));
- _insert_feature("character_variant_94", HB_TAG('c', 'v', '9', '4'));
- _insert_feature("character_variant_95", HB_TAG('c', 'v', '9', '5'));
- _insert_feature("character_variant_96", HB_TAG('c', 'v', '9', '6'));
- _insert_feature("character_variant_97", HB_TAG('c', 'v', '9', '7'));
- _insert_feature("character_variant_98", HB_TAG('c', 'v', '9', '8'));
- _insert_feature("character_variant_99", HB_TAG('c', 'v', '9', '9'));
- _insert_feature("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'));
- _insert_feature("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'));
- _insert_feature("distances", HB_TAG('d', 'i', 's', 't'));
- _insert_feature("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'));
- _insert_feature("denominators", HB_TAG('d', 'n', 'o', 'm'));
- _insert_feature("dotless_forms", HB_TAG('d', 't', 'l', 's'));
- _insert_feature("expert_forms", HB_TAG('e', 'x', 'p', 't'));
- _insert_feature("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'));
- _insert_feature("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'));
- _insert_feature("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'));
- _insert_feature("terminal_forms", HB_TAG('f', 'i', 'n', 'a'));
- _insert_feature("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'));
- _insert_feature("fractions", HB_TAG('f', 'r', 'a', 'c'));
- _insert_feature("full_widths", HB_TAG('f', 'w', 'i', 'd'));
- _insert_feature("half_forms", HB_TAG('h', 'a', 'l', 'f'));
- _insert_feature("halant_forms", HB_TAG('h', 'a', 'l', 'n'));
- _insert_feature("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'));
- _insert_feature("historical_forms", HB_TAG('h', 'i', 's', 't'));
- _insert_feature("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'));
- _insert_feature("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'));
- _insert_feature("hangul", HB_TAG('h', 'n', 'g', 'l'));
- _insert_feature("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'));
- _insert_feature("half_widths", HB_TAG('h', 'w', 'i', 'd'));
- _insert_feature("initial_forms", HB_TAG('i', 'n', 'i', 't'));
- _insert_feature("isolated_forms", HB_TAG('i', 's', 'o', 'l'));
- _insert_feature("italics", HB_TAG('i', 't', 'a', 'l'));
- _insert_feature("justification_alternates", HB_TAG('j', 'a', 'l', 't'));
- _insert_feature("jis78_forms", HB_TAG('j', 'p', '7', '8'));
- _insert_feature("jis83_forms", HB_TAG('j', 'p', '8', '3'));
- _insert_feature("jis90_forms", HB_TAG('j', 'p', '9', '0'));
- _insert_feature("jis2004_forms", HB_TAG('j', 'p', '0', '4'));
- _insert_feature("kerning", HB_TAG('k', 'e', 'r', 'n'));
- _insert_feature("left_bounds", HB_TAG('l', 'f', 'b', 'd'));
- _insert_feature("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'));
- _insert_feature("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'));
- _insert_feature("lining_figures", HB_TAG('l', 'n', 'u', 'm'));
- _insert_feature("localized_forms", HB_TAG('l', 'o', 'c', 'l'));
- _insert_feature("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'));
- _insert_feature("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'));
- _insert_feature("mark_positioning", HB_TAG('m', 'a', 'r', 'k'));
- _insert_feature("medial_forms_2", HB_TAG('m', 'e', 'd', '2'));
- _insert_feature("medial_forms", HB_TAG('m', 'e', 'd', 'i'));
- _insert_feature("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'));
- _insert_feature("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'));
- _insert_feature("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'));
- _insert_feature("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'));
- _insert_feature("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'));
- _insert_feature("nukta_forms", HB_TAG('n', 'u', 'k', 't'));
- _insert_feature("numerators", HB_TAG('n', 'u', 'm', 'r'));
- _insert_feature("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'));
- _insert_feature("optical_bounds", HB_TAG('o', 'p', 'b', 'd'));
- _insert_feature("ordinals", HB_TAG('o', 'r', 'd', 'n'));
- _insert_feature("ornaments", HB_TAG('o', 'r', 'n', 'm'));
- _insert_feature("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'));
- _insert_feature("petite_capitals", HB_TAG('p', 'c', 'a', 'p'));
- _insert_feature("proportional_kana", HB_TAG('p', 'k', 'n', 'a'));
- _insert_feature("proportional_figures", HB_TAG('p', 'n', 'u', 'm'));
- _insert_feature("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'));
- _insert_feature("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'));
- _insert_feature("post_base_forms", HB_TAG('p', 's', 't', 'f'));
- _insert_feature("post_base_substitutions", HB_TAG('p', 's', 't', 's'));
- _insert_feature("proportional_widths", HB_TAG('p', 'w', 'i', 'd'));
- _insert_feature("quarter_widths", HB_TAG('q', 'w', 'i', 'd'));
- _insert_feature("randomize", HB_TAG('r', 'a', 'n', 'd'));
- _insert_feature("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'));
- _insert_feature("rakar_forms", HB_TAG('r', 'k', 'r', 'f'));
- _insert_feature("required_ligatures", HB_TAG('r', 'l', 'i', 'g'));
- _insert_feature("reph_forms", HB_TAG('r', 'p', 'h', 'f'));
- _insert_feature("right_bounds", HB_TAG('r', 't', 'b', 'd'));
- _insert_feature("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'));
- _insert_feature("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'));
- _insert_feature("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'));
- _insert_feature("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'));
- _insert_feature("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'));
- _insert_feature("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'));
- _insert_feature("optical_size", HB_TAG('s', 'i', 'z', 'e'));
- _insert_feature("small_capitals", HB_TAG('s', 'm', 'c', 'p'));
- _insert_feature("simplified_forms", HB_TAG('s', 'm', 'p', 'l'));
- _insert_feature("stylistic_set_01", HB_TAG('s', 's', '0', '1'));
- _insert_feature("stylistic_set_02", HB_TAG('s', 's', '0', '2'));
- _insert_feature("stylistic_set_03", HB_TAG('s', 's', '0', '3'));
- _insert_feature("stylistic_set_04", HB_TAG('s', 's', '0', '4'));
- _insert_feature("stylistic_set_05", HB_TAG('s', 's', '0', '5'));
- _insert_feature("stylistic_set_06", HB_TAG('s', 's', '0', '6'));
- _insert_feature("stylistic_set_07", HB_TAG('s', 's', '0', '7'));
- _insert_feature("stylistic_set_08", HB_TAG('s', 's', '0', '8'));
- _insert_feature("stylistic_set_09", HB_TAG('s', 's', '0', '9'));
- _insert_feature("stylistic_set_10", HB_TAG('s', 's', '1', '0'));
- _insert_feature("stylistic_set_11", HB_TAG('s', 's', '1', '1'));
- _insert_feature("stylistic_set_12", HB_TAG('s', 's', '1', '2'));
- _insert_feature("stylistic_set_13", HB_TAG('s', 's', '1', '3'));
- _insert_feature("stylistic_set_14", HB_TAG('s', 's', '1', '4'));
- _insert_feature("stylistic_set_15", HB_TAG('s', 's', '1', '5'));
- _insert_feature("stylistic_set_16", HB_TAG('s', 's', '1', '6'));
- _insert_feature("stylistic_set_17", HB_TAG('s', 's', '1', '7'));
- _insert_feature("stylistic_set_18", HB_TAG('s', 's', '1', '8'));
- _insert_feature("stylistic_set_19", HB_TAG('s', 's', '1', '9'));
- _insert_feature("stylistic_set_20", HB_TAG('s', 's', '2', '0'));
- _insert_feature("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'));
- _insert_feature("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'));
- _insert_feature("subscript", HB_TAG('s', 'u', 'b', 's'));
- _insert_feature("superscript", HB_TAG('s', 'u', 'p', 's'));
- _insert_feature("swash", HB_TAG('s', 'w', 's', 'h'));
- _insert_feature("titling", HB_TAG('t', 'i', 't', 'l'));
- _insert_feature("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'));
- _insert_feature("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'));
- _insert_feature("tabular_figures", HB_TAG('t', 'n', 'u', 'm'));
- _insert_feature("traditional_forms", HB_TAG('t', 'r', 'a', 'd'));
- _insert_feature("third_widths", HB_TAG('t', 'w', 'i', 'd'));
- _insert_feature("unicase", HB_TAG('u', 'n', 'i', 'c'));
- _insert_feature("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'));
- _insert_feature("vattu_variants", HB_TAG('v', 'a', 't', 'u'));
- _insert_feature("vertical_writing", HB_TAG('v', 'e', 'r', 't'));
- _insert_feature("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'));
- _insert_feature("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'));
- _insert_feature("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'));
- _insert_feature("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'));
- _insert_feature("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'));
- _insert_feature("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'));
- _insert_feature("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'));
- _insert_feature("slashed_zero", HB_TAG('z', 'e', 'r', 'o'));
+ // Name, Tag, Data Type, Hidden
+ _insert_feature("access_all_alternates", HB_TAG('a', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("above_base_forms", HB_TAG('a', 'b', 'v', 'f'), Variant::Type::INT, true);
+ _insert_feature("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'), Variant::Type::INT, true);
+ _insert_feature("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'), Variant::Type::INT, true);
+ _insert_feature("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'), Variant::Type::INT, false);
+ _insert_feature("akhands", HB_TAG('a', 'k', 'h', 'n'), Variant::Type::INT, true);
+ _insert_feature("below_base_forms", HB_TAG('b', 'l', 'w', 'f'), Variant::Type::INT, true);
+ _insert_feature("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'), Variant::Type::INT, true);
+ _insert_feature("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'), Variant::Type::INT, true);
+ _insert_feature("contextual_alternates", HB_TAG('c', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'), Variant::Type::BOOL, false);
+ _insert_feature("glyph_composition", HB_TAG('c', 'c', 'm', 'p'), Variant::Type::INT, true);
+ _insert_feature("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'), Variant::Type::INT, true);
+ _insert_feature("contextual_half_width_spacing", HB_TAG('c', 'h', 'w', 's'), Variant::Type::INT, true);
+ _insert_feature("conjunct_forms", HB_TAG('c', 'j', 'c', 't'), Variant::Type::INT, true);
+ _insert_feature("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'), Variant::Type::BOOL, false);
+ _insert_feature("capital_spacing", HB_TAG('c', 'p', 's', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("contextual_swash", HB_TAG('c', 's', 'w', 'h'), Variant::Type::INT, false);
+ _insert_feature("cursive_positioning", HB_TAG('c', 'u', 'r', 's'), Variant::Type::INT, true);
+ _insert_feature("character_variant_01", HB_TAG('c', 'v', '0', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_02", HB_TAG('c', 'v', '0', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_03", HB_TAG('c', 'v', '0', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_04", HB_TAG('c', 'v', '0', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_05", HB_TAG('c', 'v', '0', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_06", HB_TAG('c', 'v', '0', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_07", HB_TAG('c', 'v', '0', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_08", HB_TAG('c', 'v', '0', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_09", HB_TAG('c', 'v', '0', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_10", HB_TAG('c', 'v', '1', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_11", HB_TAG('c', 'v', '1', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_12", HB_TAG('c', 'v', '1', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_13", HB_TAG('c', 'v', '1', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_14", HB_TAG('c', 'v', '1', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_15", HB_TAG('c', 'v', '1', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_16", HB_TAG('c', 'v', '1', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_17", HB_TAG('c', 'v', '1', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_18", HB_TAG('c', 'v', '1', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_19", HB_TAG('c', 'v', '1', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_20", HB_TAG('c', 'v', '2', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_21", HB_TAG('c', 'v', '2', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_22", HB_TAG('c', 'v', '2', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_23", HB_TAG('c', 'v', '2', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_24", HB_TAG('c', 'v', '2', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_25", HB_TAG('c', 'v', '2', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_26", HB_TAG('c', 'v', '2', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_27", HB_TAG('c', 'v', '2', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_28", HB_TAG('c', 'v', '2', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_29", HB_TAG('c', 'v', '2', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_30", HB_TAG('c', 'v', '3', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_31", HB_TAG('c', 'v', '3', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_32", HB_TAG('c', 'v', '3', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_33", HB_TAG('c', 'v', '3', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_34", HB_TAG('c', 'v', '3', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_35", HB_TAG('c', 'v', '3', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_36", HB_TAG('c', 'v', '3', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_37", HB_TAG('c', 'v', '3', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_38", HB_TAG('c', 'v', '3', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_39", HB_TAG('c', 'v', '3', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_40", HB_TAG('c', 'v', '4', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_41", HB_TAG('c', 'v', '4', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_42", HB_TAG('c', 'v', '4', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_43", HB_TAG('c', 'v', '4', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_44", HB_TAG('c', 'v', '4', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_45", HB_TAG('c', 'v', '4', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_46", HB_TAG('c', 'v', '4', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_47", HB_TAG('c', 'v', '4', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_48", HB_TAG('c', 'v', '4', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_49", HB_TAG('c', 'v', '4', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_50", HB_TAG('c', 'v', '5', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_51", HB_TAG('c', 'v', '5', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_52", HB_TAG('c', 'v', '5', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_53", HB_TAG('c', 'v', '5', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_54", HB_TAG('c', 'v', '5', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_55", HB_TAG('c', 'v', '5', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_56", HB_TAG('c', 'v', '5', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_57", HB_TAG('c', 'v', '5', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_58", HB_TAG('c', 'v', '5', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_59", HB_TAG('c', 'v', '5', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_60", HB_TAG('c', 'v', '6', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_61", HB_TAG('c', 'v', '6', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_62", HB_TAG('c', 'v', '6', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_63", HB_TAG('c', 'v', '6', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_64", HB_TAG('c', 'v', '6', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_65", HB_TAG('c', 'v', '6', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_66", HB_TAG('c', 'v', '6', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_67", HB_TAG('c', 'v', '6', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_68", HB_TAG('c', 'v', '6', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_69", HB_TAG('c', 'v', '6', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_70", HB_TAG('c', 'v', '7', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_71", HB_TAG('c', 'v', '7', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_72", HB_TAG('c', 'v', '7', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_73", HB_TAG('c', 'v', '7', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_74", HB_TAG('c', 'v', '7', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_75", HB_TAG('c', 'v', '7', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_76", HB_TAG('c', 'v', '7', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_77", HB_TAG('c', 'v', '7', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_78", HB_TAG('c', 'v', '7', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_79", HB_TAG('c', 'v', '7', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_80", HB_TAG('c', 'v', '8', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_81", HB_TAG('c', 'v', '8', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_82", HB_TAG('c', 'v', '8', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_83", HB_TAG('c', 'v', '8', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_84", HB_TAG('c', 'v', '8', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_85", HB_TAG('c', 'v', '8', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_86", HB_TAG('c', 'v', '8', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_87", HB_TAG('c', 'v', '8', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_88", HB_TAG('c', 'v', '8', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_89", HB_TAG('c', 'v', '8', '9'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_90", HB_TAG('c', 'v', '9', '0'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_91", HB_TAG('c', 'v', '9', '1'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_92", HB_TAG('c', 'v', '9', '2'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_93", HB_TAG('c', 'v', '9', '3'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_94", HB_TAG('c', 'v', '9', '4'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_95", HB_TAG('c', 'v', '9', '5'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_96", HB_TAG('c', 'v', '9', '6'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_97", HB_TAG('c', 'v', '9', '7'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_98", HB_TAG('c', 'v', '9', '8'), Variant::Type::BOOL, false);
+ _insert_feature("character_variant_99", HB_TAG('c', 'v', '9', '9'), Variant::Type::BOOL, false);
+ _insert_feature("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("distances", HB_TAG('d', 'i', 's', 't'), Variant::Type::INT, true);
+ _insert_feature("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("denominators", HB_TAG('d', 'n', 'o', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("dotless_forms", HB_TAG('d', 't', 'l', 's'), Variant::Type::INT, true);
+ _insert_feature("expert_forms", HB_TAG('e', 'x', 'p', 't'), Variant::Type::BOOL, true);
+ _insert_feature("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'), Variant::Type::INT, true);
+ _insert_feature("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'), Variant::Type::INT, true);
+ _insert_feature("terminal_forms", HB_TAG('f', 'i', 'n', 'a'), Variant::Type::INT, true);
+ _insert_feature("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'), Variant::Type::INT, true);
+ _insert_feature("fractions", HB_TAG('f', 'r', 'a', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("full_widths", HB_TAG('f', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("half_forms", HB_TAG('h', 'a', 'l', 'f'), Variant::Type::INT, true);
+ _insert_feature("halant_forms", HB_TAG('h', 'a', 'l', 'n'), Variant::Type::INT, true);
+ _insert_feature("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("historical_forms", HB_TAG('h', 'i', 's', 't'), Variant::Type::INT, false);
+ _insert_feature("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'), Variant::Type::BOOL, false);
+ _insert_feature("hangul", HB_TAG('h', 'n', 'g', 'l'), Variant::Type::INT, false);
+ _insert_feature("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'), Variant::Type::INT, false);
+ _insert_feature("half_widths", HB_TAG('h', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("initial_forms", HB_TAG('i', 'n', 'i', 't'), Variant::Type::INT, true);
+ _insert_feature("isolated_forms", HB_TAG('i', 's', 'o', 'l'), Variant::Type::INT, true);
+ _insert_feature("italics", HB_TAG('i', 't', 'a', 'l'), Variant::Type::INT, false);
+ _insert_feature("justification_alternates", HB_TAG('j', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("jis78_forms", HB_TAG('j', 'p', '7', '8'), Variant::Type::INT, false);
+ _insert_feature("jis83_forms", HB_TAG('j', 'p', '8', '3'), Variant::Type::INT, false);
+ _insert_feature("jis90_forms", HB_TAG('j', 'p', '9', '0'), Variant::Type::INT, false);
+ _insert_feature("jis2004_forms", HB_TAG('j', 'p', '0', '4'), Variant::Type::INT, false);
+ _insert_feature("kerning", HB_TAG('k', 'e', 'r', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("left_bounds", HB_TAG('l', 'f', 'b', 'd'), Variant::Type::INT, false);
+ _insert_feature("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("lining_figures", HB_TAG('l', 'n', 'u', 'm'), Variant::Type::INT, false);
+ _insert_feature("localized_forms", HB_TAG('l', 'o', 'c', 'l'), Variant::Type::INT, true);
+ _insert_feature("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'), Variant::Type::INT, true);
+ _insert_feature("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'), Variant::Type::INT, true);
+ _insert_feature("mark_positioning", HB_TAG('m', 'a', 'r', 'k'), Variant::Type::INT, true);
+ _insert_feature("medial_forms_2", HB_TAG('m', 'e', 'd', '2'), Variant::Type::INT, true);
+ _insert_feature("medial_forms", HB_TAG('m', 'e', 'd', 'i'), Variant::Type::INT, true);
+ _insert_feature("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'), Variant::Type::BOOL, false);
+ _insert_feature("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'), Variant::Type::INT, true);
+ _insert_feature("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'), Variant::Type::INT, true);
+ _insert_feature("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'), Variant::Type::INT, false);
+ _insert_feature("nukta_forms", HB_TAG('n', 'u', 'k', 't'), Variant::Type::INT, true);
+ _insert_feature("numerators", HB_TAG('n', 'u', 'm', 'r'), Variant::Type::BOOL, false);
+ _insert_feature("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'), Variant::Type::INT, false);
+ _insert_feature("optical_bounds", HB_TAG('o', 'p', 'b', 'd'), Variant::Type::INT, true);
+ _insert_feature("ordinals", HB_TAG('o', 'r', 'd', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("ornaments", HB_TAG('o', 'r', 'n', 'm'), Variant::Type::INT, false);
+ _insert_feature("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'), Variant::Type::BOOL, false);
+ _insert_feature("petite_capitals", HB_TAG('p', 'c', 'a', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_kana", HB_TAG('p', 'k', 'n', 'a'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_figures", HB_TAG('p', 'n', 'u', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'), Variant::Type::INT, true);
+ _insert_feature("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'), Variant::Type::INT, true);
+ _insert_feature("post_base_forms", HB_TAG('p', 's', 't', 'f'), Variant::Type::INT, true);
+ _insert_feature("post_base_substitutions", HB_TAG('p', 's', 't', 's'), Variant::Type::INT, true);
+ _insert_feature("proportional_widths", HB_TAG('p', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("quarter_widths", HB_TAG('q', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("randomize", HB_TAG('r', 'a', 'n', 'd'), Variant::Type::INT, false);
+ _insert_feature("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'), Variant::Type::BOOL, true);
+ _insert_feature("rakar_forms", HB_TAG('r', 'k', 'r', 'f'), Variant::Type::INT, true);
+ _insert_feature("required_ligatures", HB_TAG('r', 'l', 'i', 'g'), Variant::Type::BOOL, true);
+ _insert_feature("reph_forms", HB_TAG('r', 'p', 'h', 'f'), Variant::Type::INT, true);
+ _insert_feature("right_bounds", HB_TAG('r', 't', 'b', 'd'), Variant::Type::INT, false);
+ _insert_feature("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'), Variant::Type::INT, true);
+ _insert_feature("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'), Variant::Type::INT, true);
+ _insert_feature("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'), Variant::Type::INT, false);
+ _insert_feature("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'), Variant::Type::INT, true);
+ _insert_feature("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'), Variant::Type::BOOL, false);
+ _insert_feature("optical_size", HB_TAG('s', 'i', 'z', 'e'), Variant::Type::INT, false);
+ _insert_feature("small_capitals", HB_TAG('s', 'm', 'c', 'p'), Variant::Type::BOOL, false);
+ _insert_feature("simplified_forms", HB_TAG('s', 'm', 'p', 'l'), Variant::Type::INT, false);
+ _insert_feature("stylistic_set_01", HB_TAG('s', 's', '0', '1'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_02", HB_TAG('s', 's', '0', '2'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_03", HB_TAG('s', 's', '0', '3'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_04", HB_TAG('s', 's', '0', '4'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_05", HB_TAG('s', 's', '0', '5'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_06", HB_TAG('s', 's', '0', '6'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_07", HB_TAG('s', 's', '0', '7'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_08", HB_TAG('s', 's', '0', '8'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_09", HB_TAG('s', 's', '0', '9'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_10", HB_TAG('s', 's', '1', '0'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_11", HB_TAG('s', 's', '1', '1'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_12", HB_TAG('s', 's', '1', '2'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_13", HB_TAG('s', 's', '1', '3'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_14", HB_TAG('s', 's', '1', '4'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_15", HB_TAG('s', 's', '1', '5'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_16", HB_TAG('s', 's', '1', '6'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_17", HB_TAG('s', 's', '1', '7'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_18", HB_TAG('s', 's', '1', '8'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_19", HB_TAG('s', 's', '1', '9'), Variant::Type::BOOL, false);
+ _insert_feature("stylistic_set_20", HB_TAG('s', 's', '2', '0'), Variant::Type::BOOL, false);
+ _insert_feature("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'), Variant::Type::INT, true);
+ _insert_feature("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'), Variant::Type::INT, true);
+ _insert_feature("subscript", HB_TAG('s', 'u', 'b', 's'), Variant::Type::BOOL, false);
+ _insert_feature("superscript", HB_TAG('s', 'u', 'p', 's'), Variant::Type::BOOL, false);
+ _insert_feature("swash", HB_TAG('s', 'w', 's', 'h'), Variant::Type::INT, false);
+ _insert_feature("titling", HB_TAG('t', 'i', 't', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'), Variant::Type::INT, false);
+ _insert_feature("tabular_figures", HB_TAG('t', 'n', 'u', 'm'), Variant::Type::BOOL, false);
+ _insert_feature("traditional_forms", HB_TAG('t', 'r', 'a', 'd'), Variant::Type::INT, false);
+ _insert_feature("third_widths", HB_TAG('t', 'w', 'i', 'd'), Variant::Type::BOOL, false);
+ _insert_feature("unicase", HB_TAG('u', 'n', 'i', 'c'), Variant::Type::BOOL, false);
+ _insert_feature("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'), Variant::Type::INT, false);
+ _insert_feature("vattu_variants", HB_TAG('v', 'a', 't', 'u'), Variant::Type::INT, true);
+ _insert_feature("vertical_contextual_half_width_spacing", HB_TAG('v', 'c', 'h', 'w'), Variant::Type::BOOL, false);
+ _insert_feature("vertical_alternates", HB_TAG('v', 'e', 'r', 't'), Variant::Type::INT, false);
+ _insert_feature("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'), Variant::Type::INT, true);
+ _insert_feature("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'), Variant::Type::INT, false);
+ _insert_feature("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'), Variant::Type::BOOL, false);
+ _insert_feature("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'), Variant::Type::BOOL, false);
+ _insert_feature("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'), Variant::Type::INT, false);
+ _insert_feature("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'), Variant::Type::INT, false);
+ _insert_feature("slashed_zero", HB_TAG('z', 'e', 'r', 'o'), Variant::Type::BOOL, false);
// Registered OpenType variation tag.
- _insert_feature("italic", HB_TAG('i', 't', 'a', 'l'));
- _insert_feature("optical_size", HB_TAG('o', 'p', 's', 'z'));
- _insert_feature("slant", HB_TAG('s', 'l', 'n', 't'));
- _insert_feature("width", HB_TAG('w', 'd', 't', 'h'));
- _insert_feature("weight", HB_TAG('w', 'g', 'h', 't'));
+ _insert_feature("italic", HB_TAG('i', 't', 'a', 'l'), Variant::Type::INT, false);
+ _insert_feature("optical_size", HB_TAG('o', 'p', 's', 'z'), Variant::Type::INT, false);
+ _insert_feature("slant", HB_TAG('s', 'l', 'n', 't'), Variant::Type::INT, false);
+ _insert_feature("width", HB_TAG('w', 'd', 't', 'h'), Variant::Type::INT, false);
+ _insert_feature("weight", HB_TAG('w', 'g', 'h', 't'), Variant::Type::INT, false);
}
int64_t TextServerAdvanced::name_to_tag(const String &p_name) const {
@@ -740,9 +748,23 @@ int64_t TextServerAdvanced::name_to_tag(const String &p_name) const {
return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
}
+Variant::Type TextServerAdvanced::_get_tag_type(int64_t p_tag) const {
+ if (feature_sets_inv.has(p_tag)) {
+ return feature_sets_inv[p_tag].vtype;
+ }
+ return Variant::Type::INT;
+}
+
+bool TextServerAdvanced::_get_tag_hidden(int64_t p_tag) const {
+ if (feature_sets_inv.has(p_tag)) {
+ return feature_sets_inv[p_tag].hidden;
+ }
+ return false;
+}
+
String TextServerAdvanced::tag_to_name(int64_t p_tag) const {
if (feature_sets_inv.has(p_tag)) {
- return feature_sets_inv[p_tag];
+ return feature_sets_inv[p_tag].name;
}
// No readable name, use tag string.
@@ -756,7 +778,7 @@ String TextServerAdvanced::tag_to_name(int64_t p_tag) const {
/* Font Glyph Rendering */
/*************************************************************************/
-_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
+_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
FontTexturePosition ret;
ret.index = -1;
@@ -953,7 +975,7 @@ void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
}
}
-_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
msdfgen::Shape shape;
shape.contours.clear();
@@ -1059,7 +1081,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
int w = bitmap.width;
int h = bitmap.rows;
@@ -1136,12 +1158,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
/* Font Cache */
/*************************************************************************/
-_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.
- FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
+ FontForSizeAdvanced *fd = p_font_data->cache[p_size];
if (fd->glyph_map.has(p_glyph)) {
return fd->glyph_map[p_glyph].found;
}
@@ -1260,13 +1282,13 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
return false;
}
-_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *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;
}
- FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced);
+ FontForSizeAdvanced *fd = memnew(FontForSizeAdvanced);
fd->size = p_size;
if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
@@ -1288,7 +1310,16 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -1329,7 +1360,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
fd->underline_position = (-FT_MulFix(fd->face->underline_position, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
- hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform.columns[0][1]);
+ hb_font_set_synthetic_slant(fd->hb_handle, p_font_data->transform[0][1]);
if (!p_font_data->face_init) {
// Get style flags and name.
@@ -1341,29 +1372,31 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
}
p_font_data->style_flags = 0;
if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
- p_font_data->style_flags |= FONT_BOLD;
+ p_font_data->style_flags.set_flag(FONT_BOLD);
}
if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
- p_font_data->style_flags |= FONT_ITALIC;
+ p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
- p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);
}
+
+ hb_face_t *hb_face = hb_font_get_face(fd->hb_handle);
// Get supported scripts from OpenType font data.
p_font_data->supported_scripts.clear();
- unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ unsigned int count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
+ hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GSUB, 0, &count, script_tags);
for (unsigned int i = 0; i < count; i++) {
p_font_data->supported_scripts.insert(script_tags[i]);
}
memfree(script_tags);
}
- count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GPOS, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
+ hb_ot_layout_table_get_script_tags(hb_face, HB_OT_TAG_GPOS, 0, &count, script_tags);
for (unsigned int i = 0; i < count; i++) {
p_font_data->supported_scripts.insert(script_tags[i]);
}
@@ -1587,21 +1620,49 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
// Read OpenType feature tags.
p_font_data->supported_features.clear();
- count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GSUB, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
+ hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GSUB, 0, &count, feature_tags);
for (unsigned int i = 0; i < count; i++) {
- p_font_data->supported_features[feature_tags[i]] = 1;
+ Dictionary ftr;
+
+ hb_ot_name_id_t lbl_id;
+ if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GSUB, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) {
+ PackedInt32Array lbl;
+ unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1;
+ lbl.resize(text_size);
+ memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size);
+ hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw());
+ ftr["label"] = String((const char32_t *)lbl.ptr());
+ }
+ ftr["type"] = _get_tag_type(feature_tags[i]);
+ ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
+
+ p_font_data->supported_features[feature_tags[i]] = ftr;
}
memfree(feature_tags);
}
- count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ count = hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GPOS, 0, nullptr, nullptr);
if (count != 0) {
hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
- hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
+ hb_ot_layout_table_get_feature_tags(hb_face, HB_OT_TAG_GPOS, 0, &count, feature_tags);
for (unsigned int i = 0; i < count; i++) {
- p_font_data->supported_features[feature_tags[i]] = 1;
+ Dictionary ftr;
+
+ hb_ot_name_id_t lbl_id;
+ if (hb_ot_layout_feature_get_name_ids(hb_face, HB_OT_TAG_GPOS, i, &lbl_id, nullptr, nullptr, nullptr, nullptr)) {
+ PackedInt32Array lbl;
+ unsigned int text_size = hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), nullptr, nullptr) + 1;
+ lbl.resize(text_size);
+ memset((uint32_t *)lbl.ptrw(), 0, sizeof(uint32_t) * text_size);
+ hb_ot_name_get_utf32(hb_face, lbl_id, hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1), &text_size, (uint32_t *)lbl.ptrw());
+ ftr["label"] = String((const char32_t *)lbl.ptr());
+ }
+ ftr["type"] = _get_tag_type(feature_tags[i]);
+ ftr["hidden"] = _get_tag_hidden(feature_tags[i]);
+
+ p_font_data->supported_features[feature_tags[i]] = ftr;
}
memfree(feature_tags);
}
@@ -1667,8 +1728,8 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
return true;
}
-_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) {
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : p_font_data->cache) {
+_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontAdvanced *p_font_data) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : p_font_data->cache) {
memdelete(E.value);
}
p_font_data->cache.clear();
@@ -1679,7 +1740,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_fo
}
hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, nullptr);
MutexLock lock(fd->mutex);
@@ -1691,13 +1752,15 @@ hb_font_t *TextServerAdvanced::_font_get_hb_handle(const RID &p_font_rid, int64_
}
RID TextServerAdvanced::create_font() {
- FontDataAdvanced *fd = memnew(FontDataAdvanced);
+ _THREAD_SAFE_METHOD_
+
+ FontAdvanced *fd = memnew(FontAdvanced);
return font_owner.make_rid(fd);
}
void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1708,7 +1771,7 @@ void TextServerAdvanced::font_set_data(const RID &p_font_rid, const PackedByteAr
}
void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1718,8 +1781,71 @@ void TextServerAdvanced::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
-void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+void TextServerAdvanced::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerAdvanced::font_get_face_index(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerAdvanced::font_get_face_count(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
+void TextServerAdvanced::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1728,8 +1854,8 @@ void TextServerAdvanced::font_set_style(const RID &p_font_rid, int64_t /*FontSty
fd->style_flags = p_style;
}
-int64_t /*FontStyle*/ TextServerAdvanced::font_get_style(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+BitField<TextServer::FontStyle> TextServerAdvanced::font_get_style(const RID &p_font_rid) const {
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1739,7 +1865,7 @@ int64_t /*FontStyle*/ TextServerAdvanced::font_get_style(const RID &p_font_rid)
}
void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String &p_name) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1749,7 +1875,7 @@ void TextServerAdvanced::font_set_style_name(const RID &p_font_rid, const String
}
String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -1759,7 +1885,7 @@ String TextServerAdvanced::font_get_style_name(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_name) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1769,7 +1895,7 @@ void TextServerAdvanced::font_set_name(const RID &p_font_rid, const String &p_na
}
String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -1779,7 +1905,7 @@ String TextServerAdvanced::font_get_name(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1790,7 +1916,7 @@ void TextServerAdvanced::font_set_antialiased(const RID &p_font_rid, bool p_anti
}
bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1798,12 +1924,12 @@ bool TextServerAdvanced::font_is_antialiased(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
if (fd->mipmaps != p_generate_mipmaps) {
- for (KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
}
@@ -1813,7 +1939,7 @@ void TextServerAdvanced::font_set_generate_mipmaps(const RID &p_font_rid, bool p
}
bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1821,7 +1947,7 @@ bool TextServerAdvanced::font_get_generate_mipmaps(const RID &p_font_rid) const
}
void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1832,7 +1958,7 @@ void TextServerAdvanced::font_set_multichannel_signed_distance_field(const RID &
}
bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1840,7 +1966,7 @@ bool TextServerAdvanced::font_is_multichannel_signed_distance_field(const RID &p
}
void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1851,7 +1977,7 @@ void TextServerAdvanced::font_set_msdf_pixel_range(const RID &p_font_rid, int64_
}
int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1859,7 +1985,7 @@ int64_t TextServerAdvanced::font_get_msdf_pixel_range(const RID &p_font_rid) con
}
void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1870,7 +1996,7 @@ void TextServerAdvanced::font_set_msdf_size(const RID &p_font_rid, int64_t p_msd
}
int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1878,7 +2004,7 @@ int64_t TextServerAdvanced::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1886,7 +2012,7 @@ void TextServerAdvanced::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
}
int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1894,7 +2020,7 @@ int64_t TextServerAdvanced::font_get_fixed_size(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1905,7 +2031,7 @@ void TextServerAdvanced::font_set_force_autohinter(const RID &p_font_rid, bool p
}
bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1913,7 +2039,7 @@ bool TextServerAdvanced::font_is_force_autohinter(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1924,7 +2050,7 @@ void TextServerAdvanced::font_set_hinting(const RID &p_font_rid, TextServer::Hin
}
TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -1932,7 +2058,7 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(const RID &p_font_rid)
}
void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1940,7 +2066,7 @@ void TextServerAdvanced::font_set_subpixel_positioning(const RID &p_font_rid, Te
}
TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positioning(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
MutexLock lock(fd->mutex);
@@ -1948,7 +2074,7 @@ TextServer::SubpixelPositioning TextServerAdvanced::font_get_subpixel_positionin
}
void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_strength) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1959,7 +2085,7 @@ void TextServerAdvanced::font_set_embolden(const RID &p_font_rid, double p_stren
}
double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1967,7 +2093,7 @@ double TextServerAdvanced::font_get_embolden(const RID &p_font_rid) const {
}
void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1978,7 +2104,7 @@ void TextServerAdvanced::font_set_transform(const RID &p_font_rid, const Transfo
}
Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
MutexLock lock(fd->mutex);
@@ -1986,7 +2112,7 @@ Transform2D TextServerAdvanced::font_get_transform(const RID &p_font_rid) const
}
void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1997,7 +2123,7 @@ void TextServerAdvanced::font_set_variation_coordinates(const RID &p_font_rid, c
}
Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2005,7 +2131,7 @@ Dictionary TextServerAdvanced::font_get_variation_coordinates(const RID &p_font_
}
void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2016,7 +2142,7 @@ void TextServerAdvanced::font_set_oversampling(const RID &p_font_rid, double p_o
}
double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2024,30 +2150,30 @@ double TextServerAdvanced::font_get_oversampling(const RID &p_font_rid) const {
}
Array TextServerAdvanced::font_get_size_cache_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerAdvanced::font_clear_size_cache(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
memdelete(E.value);
}
fd->cache.clear();
}
void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2058,7 +2184,7 @@ void TextServerAdvanced::font_remove_size_cache(const RID &p_font_rid, const Vec
}
void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2069,7 +2195,7 @@ void TextServerAdvanced::font_set_ascent(const RID &p_font_rid, int64_t p_size,
}
double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2085,7 +2211,7 @@ double TextServerAdvanced::font_get_ascent(const RID &p_font_rid, int64_t p_size
}
void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -2095,7 +2221,7 @@ void TextServerAdvanced::font_set_descent(const RID &p_font_rid, int64_t p_size,
}
double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2111,7 +2237,7 @@ double TextServerAdvanced::font_get_descent(const RID &p_font_rid, int64_t p_siz
}
void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2122,7 +2248,7 @@ void TextServerAdvanced::font_set_underline_position(const RID &p_font_rid, int6
}
double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2138,7 +2264,7 @@ double TextServerAdvanced::font_get_underline_position(const RID &p_font_rid, in
}
void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2149,7 +2275,7 @@ void TextServerAdvanced::font_set_underline_thickness(const RID &p_font_rid, int
}
double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2165,7 +2291,7 @@ double TextServerAdvanced::font_get_underline_thickness(const RID &p_font_rid, i
}
void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2181,7 +2307,7 @@ void TextServerAdvanced::font_set_scale(const RID &p_font_rid, int64_t p_size, d
}
double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2196,60 +2322,8 @@ double TextServerAdvanced::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-void TextServerAdvanced::font_set_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing, int64_t p_value) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND(!fd);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- fd->cache[size]->spacing_glyph = p_value;
- } break;
- case TextServer::SPACING_SPACE: {
- fd->cache[size]->spacing_space = p_value;
- } break;
- default: {
- ERR_FAIL_MSG("Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
-}
-
-int64_t TextServerAdvanced::font_get_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, 0);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
-
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_glyph;
- }
- } break;
- case TextServer::SPACING_SPACE: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_space * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_space;
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(0, "Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
- return 0;
-}
-
int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -2261,7 +2335,7 @@ int64_t TextServerAdvanced::font_get_texture_count(const RID &p_font_rid, const
}
void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
@@ -2271,7 +2345,7 @@ void TextServerAdvanced::font_clear_textures(const RID &p_font_rid, const Vector
}
void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2283,7 +2357,7 @@ void TextServerAdvanced::font_remove_texture(const RID &p_font_rid, const Vector
}
void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -2309,14 +2383,12 @@ void TextServerAdvanced::font_set_texture_image(const RID &p_font_rid, const Vec
img->generate_mipmaps();
}
- tex.texture = Ref<ImageTexture>();
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
tex.dirty = false;
}
Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -2333,7 +2405,7 @@ Ref<Image> TextServerAdvanced::font_get_texture_image(const RID &p_font_rid, con
}
void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2349,7 +2421,7 @@ void TextServerAdvanced::font_set_texture_offsets(const RID &p_font_rid, const V
}
PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -2362,7 +2434,7 @@ PackedInt32Array TextServerAdvanced::font_get_texture_offsets(const RID &p_font_
}
Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2378,7 +2450,7 @@ Array TextServerAdvanced::font_get_glyph_list(const RID &p_font_rid, const Vecto
}
void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2389,7 +2461,7 @@ void TextServerAdvanced::font_clear_glyphs(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2400,7 +2472,7 @@ void TextServerAdvanced::font_remove_glyph(const RID &p_font_rid, const Vector2i
}
double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) const {
- const FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ const FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -2414,7 +2486,7 @@ double TextServerAdvanced::_get_extra_advance(RID p_font_rid, int p_font_size) c
}
Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2442,7 +2514,7 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(const RID &p_font_rid, int64_
}
void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2457,7 +2529,7 @@ void TextServerAdvanced::font_set_glyph_advance(const RID &p_font_rid, int64_t p
}
Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2478,7 +2550,7 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(const RID &p_font_rid, const V
}
void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2493,7 +2565,7 @@ void TextServerAdvanced::font_set_glyph_offset(const RID &p_font_rid, const Vect
}
Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2514,7 +2586,7 @@ Vector2 TextServerAdvanced::font_get_glyph_size(const RID &p_font_rid, const Vec
}
void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2529,7 +2601,7 @@ void TextServerAdvanced::font_set_glyph_size(const RID &p_font_rid, const Vector
}
Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -2545,7 +2617,7 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
}
void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2560,7 +2632,7 @@ void TextServerAdvanced::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
}
int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -2576,7 +2648,7 @@ int64_t TextServerAdvanced::font_get_glyph_texture_idx(const RID &p_font_rid, co
}
void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2591,7 +2663,7 @@ void TextServerAdvanced::font_set_glyph_texture_idx(const RID &p_font_rid, const
}
RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
MutexLock lock(fd->mutex);
@@ -2616,8 +2688,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2631,7 +2702,7 @@ RID TextServerAdvanced::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
MutexLock lock(fd->mutex);
@@ -2656,8 +2727,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2671,7 +2741,7 @@ Size2 TextServerAdvanced::font_get_glyph_texture_size(const RID &p_font_rid, con
}
Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2688,13 +2758,22 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in
int error = FT_Load_Glyph(fd->cache[size]->face, index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
ERR_FAIL_COND_V(error, Dictionary());
- double h = fd->cache[size]->ascent;
+ if (fd->embolden != 0.f) {
+ FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64).
+ FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength);
+ }
+
+ if (fd->transform != Transform2D()) {
+ FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).
+ FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat);
+ }
+
double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
scale = scale * (double)p_size / (double)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
@@ -2712,7 +2791,7 @@ Dictionary TextServerAdvanced::font_get_glyph_contours(const RID &p_font_rid, in
}
Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2721,14 +2800,14 @@ Array TextServerAdvanced::font_get_kerning_list(const RID &p_font_rid, int64_t p
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2739,7 +2818,7 @@ void TextServerAdvanced::font_clear_kerning_map(const RID &p_font_rid, int64_t p
}
void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2750,7 +2829,7 @@ void TextServerAdvanced::font_remove_kerning(const RID &p_font_rid, int64_t p_si
}
void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2761,7 +2840,7 @@ void TextServerAdvanced::font_set_kerning(const RID &p_font_rid, int64_t p_size,
}
Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2769,7 +2848,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -2794,7 +2873,7 @@ Vector2 TextServerAdvanced::font_get_kerning(const RID &p_font_rid, int64_t p_si
}
int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
ERR_FAIL_COND_V_MSG((p_variation_selector >= 0xd800 && p_variation_selector <= 0xdfff) || (p_variation_selector > 0x10ffff), 0, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_variation_selector, 16) + ".");
@@ -2819,7 +2898,7 @@ int64_t TextServerAdvanced::font_get_glyph_index(const RID &p_font_rid, int64_t
}
bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
@@ -2827,7 +2906,7 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeAdvanced *at_size = fd->cache.begin()->value;
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -2838,14 +2917,14 @@ bool TextServerAdvanced::font_has_char(const RID &p_font_rid, int64_t p_char) co
}
String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeAdvanced *at_size = fd->cache.begin()->value;
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -2871,7 +2950,7 @@ String TextServerAdvanced::font_get_supported_chars(const RID &p_font_rid) const
}
void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");
ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");
@@ -2904,7 +2983,7 @@ void TextServerAdvanced::font_render_range(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2933,7 +3012,7 @@ void TextServerAdvanced::font_render_glyph(const RID &p_font_rid, const Vector2i
}
void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2979,8 +3058,7 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -3012,7 +3090,7 @@ void TextServerAdvanced::font_draw_glyph(const RID &p_font_rid, const RID &p_can
}
void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3058,8 +3136,7 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -3091,7 +3168,7 @@ void TextServerAdvanced::font_draw_glyph_outline(const RID &p_font_rid, const RI
}
bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3103,7 +3180,7 @@ bool TextServerAdvanced::font_is_language_supported(const RID &p_font_rid, const
}
void TextServerAdvanced::font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3111,7 +3188,7 @@ void TextServerAdvanced::font_set_language_support_override(const RID &p_font_ri
}
bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3119,7 +3196,7 @@ bool TextServerAdvanced::font_get_language_support_override(const RID &p_font_ri
}
void TextServerAdvanced::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3127,7 +3204,7 @@ void TextServerAdvanced::font_remove_language_support_override(const RID &p_font
}
PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -3139,7 +3216,7 @@ PackedStringArray TextServerAdvanced::font_get_language_support_overrides(const
}
bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3153,7 +3230,7 @@ bool TextServerAdvanced::font_is_script_supported(const RID &p_font_rid, const S
}
void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3161,7 +3238,7 @@ void TextServerAdvanced::font_set_script_support_override(const RID &p_font_rid,
}
bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -3169,7 +3246,7 @@ bool TextServerAdvanced::font_get_script_support_override(const RID &p_font_rid,
}
void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3177,7 +3254,7 @@ void TextServerAdvanced::font_remove_script_support_override(const RID &p_font_r
}
PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RID &p_font_rid) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -3189,7 +3266,7 @@ PackedStringArray TextServerAdvanced::font_get_script_support_overrides(const RI
}
void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -3199,7 +3276,7 @@ void TextServerAdvanced::font_set_opentype_feature_overrides(const RID &p_font_r
}
Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3207,7 +3284,7 @@ Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(const RID &p_
}
Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3217,7 +3294,7 @@ Dictionary TextServerAdvanced::font_supported_feature_list(const RID &p_font_rid
}
Dictionary TextServerAdvanced::font_supported_variation_list(const RID &p_font_rid) const {
- FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ FontAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -3261,7 +3338,7 @@ void TextServerAdvanced::font_set_global_oversampling(double p_oversampling) {
int64_t TextServerAdvanced::_convert_pos(const String &p_utf32, const Char16String &p_utf16, int64_t p_pos) const {
int64_t limit = p_pos;
if (p_utf32.length() != p_utf16.length()) {
- const UChar *data = p_utf16.ptr();
+ const UChar *data = p_utf16.get_data();
for (int i = 0; i < p_pos; i++) {
if (U16_IS_LEAD(data[i])) {
limit--;
@@ -3350,6 +3427,7 @@ void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
sd->hb_buffer = hb_buffer_create();
sd->direction = p_direction;
@@ -3496,6 +3574,31 @@ bool TextServerAdvanced::shaped_text_get_preserve_control(const RID &p_shaped) c
return sd->preserve_control;
}
+void TextServerAdvanced::shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->extra_spacing[p_spacing] != p_value) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->extra_spacing[p_spacing] = p_value;
+ invalidate(sd, false);
+ }
+}
+
+int64_t TextServerAdvanced::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+
+ MutexLock lock(sd->mutex);
+ return sd->extra_spacing[p_spacing];
+}
+
TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(const RID &p_shaped) const {
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
@@ -3739,6 +3842,8 @@ void TextServerAdvanced::_realign(ShapedTextDataAdvanced *p_sd) const {
}
RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -3761,6 +3866,9 @@ RID TextServerAdvanced::shaped_text_substr(const RID &p_shaped, int64_t p_start,
new_sd->direction = sd->direction;
new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
if (!_shape_substr(new_sd, sd, p_start, p_length)) {
memdelete(new_sd);
@@ -3886,7 +3994,7 @@ RID TextServerAdvanced::shaped_text_get_parent(const RID &p_shaped) const {
return sd->parent;
}
-double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) {
+double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
@@ -3902,7 +4010,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
int start_pos = 0;
int end_pos = sd->glyphs.size() - 1;
- if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
start = sd->glyphs.size() - 1;
@@ -3928,7 +4036,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
double justification_width;
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
if (sd->overrun_trim_data.trim_pos >= 0) {
if (sd->para_direction == DIRECTION_RTL) {
start_pos = sd->overrun_trim_data.trim_pos;
@@ -3943,7 +4051,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
justification_width = sd->width;
}
- if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
@@ -3982,7 +4090,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
- if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
+ if ((elongation_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_KASHIDA)) {
double delta_width_per_kashida = (p_width - justification_width) / elongation_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
@@ -4003,7 +4111,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
}
- if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {
double delta_width_per_space = (p_width - justification_width) / space_count;
double adv_remain = 0;
for (int i = start_pos; i <= end_pos; i++) {
@@ -4036,7 +4144,7 @@ double TextServerAdvanced::shaped_text_fit_to_width(const RID &p_shaped, double
sd->fit_width_minimum_reached = true;
}
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
sd->width = justification_width;
}
@@ -4099,7 +4207,7 @@ double TextServerAdvanced::shaped_text_tab_align(const RID &p_shaped, const Pack
return 0.0;
}
-void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) {
+void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
@@ -4111,14 +4219,14 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
- bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
- bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
- bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
- bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+ bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);
+ bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);
+ bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);
+ bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
sd->overrun_trim_data.trim_pos = -1;
sd->overrun_trim_data.ellipsis_pos = -1;
return;
@@ -4171,7 +4279,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -4319,7 +4427,7 @@ bool TextServerAdvanced::shaped_text_update_breaks(const RID &p_shaped) {
i++;
}
int r_end = sd->spans[i].end;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, (language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int j = r_start; j < r_end; j++) {
@@ -4765,12 +4873,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
RID f = p_fonts[p_fb_index];
- FontDataAdvanced *fd = font_owner.get_or_null(f);
+ FontAdvanced *fd = font_owner.get_or_null(f);
+ ERR_FAIL_COND(!fd);
+ MutexLock lock(fd->mutex);
+
Vector2i fss = _get_size(fd, fs);
hb_font_t *hb_font = _font_get_hb_handle(f, fs);
double scale = font_get_scale(f, fs);
- double sp_sp = font_get_spacing(f, fs, SPACING_SPACE);
- double sp_gl = font_get_spacing(f, fs, SPACING_GLYPH);
+ double sp_sp = p_sd->extra_spacing[SPACING_SPACE];
+ double sp_gl = p_sd->extra_spacing[SPACING_GLYPH];
+ bool last_run = (p_sd->end == p_end);
double ea = _get_extra_advance(f, fs);
bool subpos = (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_HALF) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_ONE_QUARTER) || (font_get_subpixel_positioning(f) == SUBPIXEL_POSITIONING_AUTO && fs <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE);
ERR_FAIL_COND(hb_font == nullptr);
@@ -4784,7 +4896,10 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
hb_buffer_set_script(p_sd->hb_buffer, p_script);
- if (!p_sd->spans[p_span].language.is_empty()) {
+ if (p_sd->spans[p_span].language.is_empty()) {
+ hb_language_t lang = hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1);
+ hb_buffer_set_language(p_sd->hb_buffer, lang);
+ } else {
hb_language_t lang = hb_language_from_string(p_sd->spans[p_span].language.ascii().get_data(), -1);
hb_buffer_set_language(p_sd->hb_buffer, lang);
}
@@ -4865,10 +4980,13 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
}
gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
}
- if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
- gl.advance += sp_sp;
- } else {
- gl.advance += sp_gl;
+ if (!last_run || i < glyph_count - 1) {
+ // Do not add extra spacing to the last glyph of the string.
+ if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
+ gl.advance += sp_sp;
+ } else {
+ gl.advance += sp_gl;
+ }
}
if (p_sd->preserve_control) {
@@ -5199,9 +5317,9 @@ Size2 TextServerAdvanced::shaped_text_get_size(const RID &p_shaped) const {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent).ceil();
+ return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
} else {
- return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width)).ceil();
+ return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], (sd->text_trimmed ? sd->width_trimmed : sd->width)).ceil();
}
}
@@ -5213,7 +5331,7 @@ double TextServerAdvanced::shaped_text_get_ascent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
- return sd->ascent;
+ return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const {
@@ -5224,7 +5342,7 @@ double TextServerAdvanced::shaped_text_get_descent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
- return sd->descent;
+ return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
double TextServerAdvanced::shaped_text_get_width(const RID &p_shaped) const {
@@ -5538,16 +5656,18 @@ String TextServerAdvanced::strip_diacritics(const String &p_string) const {
}
String TextServerAdvanced::string_to_upper(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
Vector<char16_t> upper;
UErrorCode err = U_ZERO_ERROR;
- int32_t len = u_strToUpper(nullptr, 0, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ int32_t len = u_strToUpper(nullptr, 0, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
upper.resize(len);
err = U_ZERO_ERROR;
- u_strToUpper(upper.ptrw(), len, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ u_strToUpper(upper.ptrw(), len, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
// Convert back to UTF-32.
@@ -5555,16 +5675,17 @@ String TextServerAdvanced::string_to_upper(const String &p_string, const String
}
String TextServerAdvanced::string_to_lower(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
Vector<char16_t> lower;
UErrorCode err = U_ZERO_ERROR;
- int32_t len = u_strToLower(nullptr, 0, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ int32_t len = u_strToLower(nullptr, 0, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, p_string, u_errorName(err));
lower.resize(len);
err = U_ZERO_ERROR;
- u_strToLower(lower.ptrw(), len, utf16.get_data(), -1, p_language.ascii().get_data(), &err);
+ u_strToLower(lower.ptrw(), len, utf16.get_data(), -1, lang.ascii().get_data(), &err);
ERR_FAIL_COND_V_MSG(U_FAILURE(err), p_string, u_errorName(err));
// Convert back to UTF-32.
@@ -5572,12 +5693,13 @@ String TextServerAdvanced::string_to_lower(const String &p_string, const String
}
PackedInt32Array TextServerAdvanced::string_get_word_breaks(const String &p_string, const String &p_language) const {
+ const String lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
// Convert to UTF-16.
Char16String utf16 = p_string.utf16();
- Set<int> breaks;
+ HashSet<int> breaks;
UErrorCode err = U_ZERO_ERROR;
- UBreakIterator *bi = ubrk_open(UBRK_LINE, p_language.ascii().get_data(), (const UChar *)utf16.ptr(), utf16.length(), &err);
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, lang.ascii().get_data(), (const UChar *)utf16.get_data(), utf16.length(), &err);
if (U_FAILURE(err)) {
// No data loaded - use fallback.
for (int i = 0; i < p_string.length(); i++) {
@@ -5626,8 +5748,10 @@ TextServerAdvanced::TextServerAdvanced() {
TextServerAdvanced::~TextServerAdvanced() {
_bmp_free_font_funcs();
+#ifdef MODULE_FREETYPE_ENABLED
if (ft_library != nullptr) {
FT_Done_FreeType(ft_library);
}
+#endif
u_cleanup();
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index c292e2e707..a772955d90 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -67,8 +67,8 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
@@ -126,19 +126,26 @@ class TextServerAdvanced : public TextServerExtension {
_THREAD_SAFE_CLASS_
struct NumSystemData {
- Set<StringName> lang;
+ HashSet<StringName> lang;
String digits;
String percent_sign;
String exp;
};
Vector<NumSystemData> num_systems;
+
+ struct FeatureInfo {
+ StringName name;
+ Variant::Type vtype = Variant::INT;
+ bool hidden = false;
+ };
+
HashMap<StringName, int32_t> feature_sets;
- HashMap<int32_t, StringName> feature_sets_inv;
+ HashMap<int32_t, FeatureInfo> feature_sets_inv;
void _insert_num_systems_lang();
void _insert_feature_sets();
- _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag);
+ _FORCE_INLINE_ void _insert_feature(const StringName &p_name, int32_t p_tag, Variant::Type p_vtype = Variant::INT, bool p_hidden = false);
// ICU support data.
@@ -176,7 +183,7 @@ class TextServerAdvanced : public TextServerExtension {
Vector2 advance;
};
- struct FontDataForSizeAdvanced {
+ struct FontForSizeAdvanced {
double ascent = 0.0;
double descent = 0.0;
double underline_position = 0.0;
@@ -184,15 +191,11 @@ class TextServerAdvanced : public TextServerExtension {
double scale = 1.0;
double oversampling = 1.0;
- int spacing_glyph = 0;
- int spacing_space = 0;
-
Vector2i size;
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
-
+ HashMap<Vector2i, Vector2> kerning_map;
hb_font_t *hb_handle = nullptr;
#ifdef MODULE_FREETYPE_ENABLED
@@ -200,7 +203,7 @@ class TextServerAdvanced : public TextServerExtension {
FT_StreamRec stream;
#endif
- ~FontDataForSizeAdvanced() {
+ ~FontForSizeAdvanced() {
if (hb_handle != nullptr) {
hb_font_destroy(hb_handle);
}
@@ -212,7 +215,7 @@ class TextServerAdvanced : public TextServerExtension {
}
};
- struct FontDataAdvanced {
+ struct FontAdvanced {
Mutex mutex;
bool antialiased = true;
@@ -229,14 +232,14 @@ class TextServerAdvanced : public TextServerExtension {
double embolden = 0.0;
Transform2D transform;
- uint32_t style_flags = 0;
+ BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
- HashMap<Vector2i, FontDataForSizeAdvanced *, VariantHasher, VariantComparator> cache;
+ HashMap<Vector2i, FontForSizeAdvanced *, VariantHasher, VariantComparator> cache;
bool face_init = false;
- Set<uint32_t> supported_scripts;
+ HashSet<uint32_t> supported_scripts;
Dictionary supported_features;
Dictionary supported_varaitions;
Dictionary feature_overrides;
@@ -248,30 +251,31 @@ class TextServerAdvanced : public TextServerExtension {
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
+ int face_index = 0;
mutable ThreadWorkPool work_pool;
- ~FontDataAdvanced() {
+ ~FontAdvanced() {
work_pool.finish();
- for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : cache) {
+ for (const KeyValue<Vector2i, FontForSizeAdvanced *> &E : cache) {
memdelete(E.value);
}
cache.clear();
}
};
- _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
#ifdef MODULE_MSDFGEN_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontAdvanced *p_font_data, FontForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
#endif
- _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
- _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const;
- _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data);
+ _FORCE_INLINE_ bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontAdvanced *p_font_data);
void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
- _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size(const FontAdvanced *p_font_data, int p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -281,7 +285,7 @@ class TextServerAdvanced : public TextServerExtension {
}
}
- _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontAdvanced *p_font_data, const Vector2i &p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -292,6 +296,8 @@ class TextServerAdvanced : public TextServerExtension {
}
_FORCE_INLINE_ double _get_extra_advance(RID p_font_rid, int p_font_size) const;
+ _FORCE_INLINE_ Variant::Type _get_tag_type(int64_t p_tag) const;
+ _FORCE_INLINE_ bool _get_tag_hidden(int64_t p_tag) const;
// Shaped text cache data.
struct TrimData {
@@ -351,6 +357,7 @@ class TextServerAdvanced : public TextServerExtension {
double descent = 0.0; // Descent for horizontal layout, 1/2 of width for vertical.
double width = 0.0; // Width for horizontal layout, height for vertical.
double width_trimmed = 0.0;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
double upos = 0.0;
double uthk = 0.0;
@@ -389,7 +396,7 @@ class TextServerAdvanced : public TextServerExtension {
// Common data.
double oversampling = 1.0;
- mutable RID_PtrOwner<FontDataAdvanced> font_owner;
+ mutable RID_PtrOwner<FontAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
void _realign(ShapedTextDataAdvanced *p_sd) const;
@@ -407,11 +414,11 @@ class TextServerAdvanced : public TextServerExtension {
static hb_font_funcs_t *funcs;
struct bmp_font_t {
- TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr;
+ TextServerAdvanced::FontForSizeAdvanced *face = nullptr;
bool unref = false; /* Whether to destroy bm_face when done. */
};
- static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
+ static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref);
static void _bmp_font_destroy(void *p_data);
static hb_bool_t _bmp_get_nominal_glyph(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_unicode, hb_codepoint_t *r_glyph, void *p_user_data);
static hb_position_t _bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
@@ -422,8 +429,8 @@ class TextServerAdvanced : public TextServerExtension {
static hb_bool_t _bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data);
static void _bmp_create_font_funcs();
static void _bmp_free_font_funcs();
- static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
- static hb_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
+ static void _bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontForSizeAdvanced *p_face, bool p_unref);
+ static hb_font_t *_bmp_font_create(TextServerAdvanced::FontForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
hb_font_t *_font_get_hb_handle(const RID &p_font, int64_t p_font_size) const;
@@ -474,8 +481,13 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
- virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
- virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
+ virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override;
+ virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override;
virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override;
virtual String font_get_style_name(const RID &p_font_rid) const override;
@@ -541,9 +553,6 @@ public:
virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override;
virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_set_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing, int64_t p_value) override;
- virtual int64_t font_get_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing) const override;
-
virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override;
@@ -641,6 +650,9 @@ public:
virtual void shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(const RID &p_shaped) const override;
+ virtual void shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) override;
+ virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const override;
+
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
@@ -652,7 +664,7 @@ public:
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;
virtual RID shaped_text_get_parent(const RID &p_shaped) const override;
- virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(const RID &p_shaped) override;
@@ -664,7 +676,7 @@ public:
virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override;
virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override;
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct
index 1753bc8b86..6c9e10db18 100644
--- a/modules/text_server_fb/gdextension_build/SConstruct
+++ b/modules/text_server_fb/gdextension_build/SConstruct
@@ -111,6 +111,7 @@ if env["freetype_enabled"]:
"src/psnames/psnames.c",
"src/raster/raster.c",
"src/sdf/sdf.c",
+ "src/svg/svg.c",
"src/smooth/smooth.c",
"src/truetype/truetype.c",
"src/type1/type1.c",
@@ -159,7 +160,14 @@ if env["freetype_enabled"]:
env_freetype.Append(CPPPATH=[thirdparty_freetype_dir + "/include", thirdparty_zlib_dir, thirdparty_png_dir])
env.Append(CPPPATH=[thirdparty_freetype_dir + "/include"])
- env_freetype.Append(CPPDEFINES=["FT2_BUILD_LIBRARY", "FT_CONFIG_OPTION_USE_PNG", ("PNG_ARM_NEON_OPT", 0)])
+ env_freetype.Append(
+ CPPDEFINES=[
+ "FT2_BUILD_LIBRARY",
+ "FT_CONFIG_OPTION_USE_PNG",
+ ("PNG_ARM_NEON_OPT", 0),
+ "FT_CONFIG_OPTION_SYSTEM_ZLIB",
+ ]
+ )
if env["target"] == "debug":
env_freetype.Append(CPPDEFINES=["ZLIB_DEBUG"])
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 4a4b51e5d3..b845beb158 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -104,7 +104,7 @@ int64_t TextServerFallback::get_features() const {
void TextServerFallback::free_rid(const RID &p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataFallback *fd = font_owner.get_or_null(p_rid);
+ FontFallback *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
@@ -199,7 +199,7 @@ String TextServerFallback::tag_to_name(int64_t p_tag) const {
/* Font Glyph Rendering */
/*************************************************************************/
-_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
+_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const {
FontTexturePosition ret;
ret.index = -1;
@@ -397,7 +397,7 @@ void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
}
}
-_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
msdfgen::Shape shape;
shape.contours.clear();
@@ -502,7 +502,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
#endif
#ifdef MODULE_FREETYPE_ENABLED
-_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
int w = bitmap.width;
int h = bitmap.rows;
@@ -579,12 +579,12 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
/* Font Cache */
/*************************************************************************/
-_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
int32_t glyph_index = p_glyph & 0xffffff; // Remove subpixel shifts.
- FontDataForSizeFallback *fd = p_font_data->cache[p_size];
+ FontForSizeFallback *fd = p_font_data->cache[p_size];
if (fd->glyph_map.has(p_glyph)) {
return fd->glyph_map[p_glyph].found;
}
@@ -705,13 +705,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
return false;
}
-_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const {
+_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontFallback *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;
}
- FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback);
+ FontForSizeFallback *fd = memnew(FontForSizeFallback);
fd->size = p_size;
if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
@@ -733,7 +733,16 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
fargs.memory_size = p_font_data->data_size;
fargs.flags = FT_OPEN_MEMORY;
fargs.stream = &fd->stream;
- error = FT_Open_Face(ft_library, &fargs, 0, &fd->face);
+
+ int max_index = 0;
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ max_index = tmp_face->num_faces - 1;
+ }
+ FT_Done_Face(tmp_face);
+
+ error = FT_Open_Face(ft_library, &fargs, CLAMP(p_font_data->face_index, 0, max_index), &fd->face);
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
@@ -782,13 +791,13 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
}
p_font_data->style_flags = 0;
if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
- p_font_data->style_flags |= FONT_BOLD;
+ p_font_data->style_flags.set_flag(FONT_BOLD);
}
if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
- p_font_data->style_flags |= FONT_ITALIC;
+ p_font_data->style_flags.set_flag(FONT_ITALIC);
}
if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
- p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ p_font_data->style_flags.set_flag(FONT_FIXED_WIDTH);
}
// Read OpenType variations.
p_font_data->supported_varaitions.clear();
@@ -842,8 +851,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
return true;
}
-_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) {
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : p_font_data->cache) {
+_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontFallback *p_font_data) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : p_font_data->cache) {
memdelete(E.value);
}
@@ -853,13 +862,15 @@ _FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_fo
}
RID TextServerFallback::create_font() {
- FontDataFallback *fd = memnew(FontDataFallback);
+ _THREAD_SAFE_METHOD_
+
+ FontFallback *fd = memnew(FontFallback);
return font_owner.make_rid(fd);
}
void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -870,7 +881,7 @@ void TextServerFallback::font_set_data(const RID &p_font_rid, const PackedByteAr
}
void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -880,8 +891,8 @@ void TextServerFallback::font_set_data_ptr(const RID &p_font_rid, const uint8_t
fd->data_size = p_data_size;
}
-void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+void TextServerFallback::font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -890,8 +901,71 @@ void TextServerFallback::font_set_style(const RID &p_font_rid, int64_t /*FontSty
fd->style_flags = p_style;
}
-int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+void TextServerFallback::font_set_face_index(const RID &p_font_rid, int64_t p_face_index) {
+ ERR_FAIL_COND(p_face_index < 0);
+ ERR_FAIL_COND(p_face_index >= 0x7FFF);
+
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->face_index != p_face_index) {
+ fd->face_index = p_face_index;
+ _font_clear_cache(fd);
+ }
+}
+
+int64_t TextServerFallback::font_get_face_index(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ return fd->face_index;
+}
+
+int64_t TextServerFallback::font_get_face_count(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ int face_count = 0;
+
+ if (fd->data_ptr && (fd->data_size > 0)) {
+ // Init dynamic font.
+#ifdef MODULE_FREETYPE_ENABLED
+ int error = 0;
+ if (!ft_library) {
+ error = FT_Init_FreeType(&ft_library);
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ FT_StreamRec stream;
+ memset(&stream, 0, sizeof(FT_StreamRec));
+ stream.base = (unsigned char *)fd->data_ptr;
+ stream.size = fd->data_size;
+ stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)fd->data_ptr;
+ fargs.memory_size = fd->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &stream;
+
+ FT_Face tmp_face;
+ error = FT_Open_Face(ft_library, &fargs, -1, &tmp_face);
+ if (error == 0) {
+ face_count = tmp_face->num_faces;
+ }
+ FT_Done_Face(tmp_face);
+#endif
+ }
+
+ return face_count;
+}
+
+BitField<TextServer::FontStyle> TextServerFallback::font_get_style(const RID &p_font_rid) const {
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -901,7 +975,7 @@ int64_t /*FontStyle*/ TextServerFallback::font_get_style(const RID &p_font_rid)
}
void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String &p_name) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -911,7 +985,7 @@ void TextServerFallback::font_set_style_name(const RID &p_font_rid, const String
}
String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -921,7 +995,7 @@ String TextServerFallback::font_get_style_name(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_name) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -931,7 +1005,7 @@ void TextServerFallback::font_set_name(const RID &p_font_rid, const String &p_na
}
String TextServerFallback::font_get_name(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -941,7 +1015,7 @@ String TextServerFallback::font_get_name(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_antialiased) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -952,7 +1026,7 @@ void TextServerFallback::font_set_antialiased(const RID &p_font_rid, bool p_anti
}
bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -960,12 +1034,12 @@ bool TextServerFallback::font_is_antialiased(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p_generate_mipmaps) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
if (fd->mipmaps != p_generate_mipmaps) {
- for (KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
for (int i = 0; i < E.value->textures.size(); i++) {
E.value->textures.write[i].dirty = true;
}
@@ -975,7 +1049,7 @@ void TextServerFallback::font_set_generate_mipmaps(const RID &p_font_rid, bool p
}
bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -983,7 +1057,7 @@ bool TextServerFallback::font_get_generate_mipmaps(const RID &p_font_rid) const
}
void TextServerFallback::font_set_multichannel_signed_distance_field(const RID &p_font_rid, bool p_msdf) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -994,7 +1068,7 @@ void TextServerFallback::font_set_multichannel_signed_distance_field(const RID &
}
bool TextServerFallback::font_is_multichannel_signed_distance_field(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1002,7 +1076,7 @@ bool TextServerFallback::font_is_multichannel_signed_distance_field(const RID &p
}
void TextServerFallback::font_set_msdf_pixel_range(const RID &p_font_rid, int64_t p_msdf_pixel_range) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1013,7 +1087,7 @@ void TextServerFallback::font_set_msdf_pixel_range(const RID &p_font_rid, int64_
}
int64_t TextServerFallback::font_get_msdf_pixel_range(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1021,7 +1095,7 @@ int64_t TextServerFallback::font_get_msdf_pixel_range(const RID &p_font_rid) con
}
void TextServerFallback::font_set_msdf_size(const RID &p_font_rid, int64_t p_msdf_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1032,7 +1106,7 @@ void TextServerFallback::font_set_msdf_size(const RID &p_font_rid, int64_t p_msd
}
int64_t TextServerFallback::font_get_msdf_size(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1040,7 +1114,7 @@ int64_t TextServerFallback::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1048,7 +1122,7 @@ void TextServerFallback::font_set_fixed_size(const RID &p_font_rid, int64_t p_fi
}
int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1056,7 +1130,7 @@ int64_t TextServerFallback::font_get_fixed_size(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_force_autohinter(const RID &p_font_rid, bool p_force_autohinter) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1067,7 +1141,7 @@ void TextServerFallback::font_set_force_autohinter(const RID &p_font_rid, bool p
}
bool TextServerFallback::font_is_force_autohinter(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1075,7 +1149,7 @@ bool TextServerFallback::font_is_force_autohinter(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1086,7 +1160,7 @@ void TextServerFallback::font_set_hinting(const RID &p_font_rid, TextServer::Hin
}
TextServer::Hinting TextServerFallback::font_get_hinting(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -1094,7 +1168,7 @@ TextServer::Hinting TextServerFallback::font_get_hinting(const RID &p_font_rid)
}
void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, TextServer::SubpixelPositioning p_subpixel) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1102,7 +1176,7 @@ void TextServerFallback::font_set_subpixel_positioning(const RID &p_font_rid, Te
}
TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positioning(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, SUBPIXEL_POSITIONING_DISABLED);
MutexLock lock(fd->mutex);
@@ -1110,7 +1184,7 @@ TextServer::SubpixelPositioning TextServerFallback::font_get_subpixel_positionin
}
void TextServerFallback::font_set_embolden(const RID &p_font_rid, double p_strength) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1121,7 +1195,7 @@ void TextServerFallback::font_set_embolden(const RID &p_font_rid, double p_stren
}
double TextServerFallback::font_get_embolden(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1129,7 +1203,7 @@ double TextServerFallback::font_get_embolden(const RID &p_font_rid) const {
}
void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transform2D &p_transform) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1140,7 +1214,7 @@ void TextServerFallback::font_set_transform(const RID &p_font_rid, const Transfo
}
Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Transform2D());
MutexLock lock(fd->mutex);
@@ -1148,7 +1222,7 @@ Transform2D TextServerFallback::font_get_transform(const RID &p_font_rid) const
}
void TextServerFallback::font_set_variation_coordinates(const RID &p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1159,7 +1233,7 @@ void TextServerFallback::font_set_variation_coordinates(const RID &p_font_rid, c
}
Dictionary TextServerFallback::font_get_variation_coordinates(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -1167,7 +1241,7 @@ Dictionary TextServerFallback::font_get_variation_coordinates(const RID &p_font_
}
void TextServerFallback::font_set_oversampling(const RID &p_font_rid, double p_oversampling) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1178,7 +1252,7 @@ void TextServerFallback::font_set_oversampling(const RID &p_font_rid, double p_o
}
double TextServerFallback::font_get_oversampling(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1186,30 +1260,30 @@ double TextServerFallback::font_get_oversampling(const RID &p_font_rid) const {
}
Array TextServerFallback::font_get_size_cache_list(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
Array ret;
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
ret.push_back(E.key);
}
return ret;
}
void TextServerFallback::font_clear_size_cache(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : fd->cache) {
memdelete(E.value);
}
fd->cache.clear();
}
void TextServerFallback::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1220,7 +1294,7 @@ void TextServerFallback::font_remove_size_cache(const RID &p_font_rid, const Vec
}
void TextServerFallback::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1231,7 +1305,7 @@ void TextServerFallback::font_set_ascent(const RID &p_font_rid, int64_t p_size,
}
double TextServerFallback::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1247,7 +1321,7 @@ double TextServerFallback::font_get_ascent(const RID &p_font_rid, int64_t p_size
}
void TextServerFallback::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -1257,7 +1331,7 @@ void TextServerFallback::font_set_descent(const RID &p_font_rid, int64_t p_size,
}
double TextServerFallback::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1273,7 +1347,7 @@ double TextServerFallback::font_get_descent(const RID &p_font_rid, int64_t p_siz
}
void TextServerFallback::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1284,7 +1358,7 @@ void TextServerFallback::font_set_underline_position(const RID &p_font_rid, int6
}
double TextServerFallback::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1300,7 +1374,7 @@ double TextServerFallback::font_get_underline_position(const RID &p_font_rid, in
}
void TextServerFallback::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1311,7 +1385,7 @@ void TextServerFallback::font_set_underline_thickness(const RID &p_font_rid, int
}
double TextServerFallback::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1327,7 +1401,7 @@ double TextServerFallback::font_get_underline_thickness(const RID &p_font_rid, i
}
void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1343,7 +1417,7 @@ void TextServerFallback::font_set_scale(const RID &p_font_rid, int64_t p_size, d
}
double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.0);
MutexLock lock(fd->mutex);
@@ -1358,60 +1432,8 @@ double TextServerFallback::font_get_scale(const RID &p_font_rid, int64_t p_size)
}
}
-void TextServerFallback::font_set_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing, int64_t p_value) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND(!fd);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- fd->cache[size]->spacing_glyph = p_value;
- } break;
- case TextServer::SPACING_SPACE: {
- fd->cache[size]->spacing_space = p_value;
- } break;
- default: {
- ERR_FAIL_MSG("Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
-}
-
-int64_t TextServerFallback::font_get_spacing(const RID &p_font_rid, int64_t p_size, TextServer::SpacingType p_spacing) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
- ERR_FAIL_COND_V(!fd, 0);
-
- MutexLock lock(fd->mutex);
- Vector2i size = _get_size(fd, p_size);
-
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
-
- switch (p_spacing) {
- case TextServer::SPACING_GLYPH: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_glyph;
- }
- } break;
- case TextServer::SPACING_SPACE: {
- if (fd->msdf) {
- return fd->cache[size]->spacing_space * (double)p_size / (double)fd->msdf_source_size;
- } else {
- return fd->cache[size]->spacing_space;
- }
- } break;
- default: {
- ERR_FAIL_V_MSG(0, "Invalid spacing type: " + String::num_int64(p_spacing));
- } break;
- }
- return 0;
-}
-
int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1423,7 +1445,7 @@ int64_t TextServerFallback::font_get_texture_count(const RID &p_font_rid, const
}
void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
@@ -1433,7 +1455,7 @@ void TextServerFallback::font_clear_textures(const RID &p_font_rid, const Vector
}
void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1445,7 +1467,7 @@ void TextServerFallback::font_remove_texture(const RID &p_font_rid, const Vector
}
void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -1471,14 +1493,12 @@ void TextServerFallback::font_set_texture_image(const RID &p_font_rid, const Vec
img->generate_mipmaps();
}
- tex.texture = Ref<ImageTexture>();
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
tex.dirty = false;
}
Ref<Image> TextServerFallback::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -1495,7 +1515,7 @@ Ref<Image> TextServerFallback::font_get_texture_image(const RID &p_font_rid, con
}
void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1511,7 +1531,7 @@ void TextServerFallback::font_set_texture_offsets(const RID &p_font_rid, const V
}
PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -1524,7 +1544,7 @@ PackedInt32Array TextServerFallback::font_get_texture_offsets(const RID &p_font_
}
Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1540,7 +1560,7 @@ Array TextServerFallback::font_get_glyph_list(const RID &p_font_rid, const Vecto
}
void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1551,7 +1571,7 @@ void TextServerFallback::font_clear_glyphs(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1562,7 +1582,7 @@ void TextServerFallback::font_remove_glyph(const RID &p_font_rid, const Vector2i
}
Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1590,7 +1610,7 @@ Vector2 TextServerFallback::font_get_glyph_advance(const RID &p_font_rid, int64_
}
void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1605,7 +1625,7 @@ void TextServerFallback::font_set_glyph_advance(const RID &p_font_rid, int64_t p
}
Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1626,7 +1646,7 @@ Vector2 TextServerFallback::font_get_glyph_offset(const RID &p_font_rid, const V
}
void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1641,7 +1661,7 @@ void TextServerFallback::font_set_glyph_offset(const RID &p_font_rid, const Vect
}
Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1662,7 +1682,7 @@ Vector2 TextServerFallback::font_get_glyph_size(const RID &p_font_rid, const Vec
}
void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1677,7 +1697,7 @@ void TextServerFallback::font_set_glyph_size(const RID &p_font_rid, const Vector
}
Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -1693,7 +1713,7 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(const RID &p_font_rid, const Ve
}
void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1708,7 +1728,7 @@ void TextServerFallback::font_set_glyph_uv_rect(const RID &p_font_rid, const Vec
}
int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -1724,7 +1744,7 @@ int64_t TextServerFallback::font_get_glyph_texture_idx(const RID &p_font_rid, co
}
void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1739,7 +1759,7 @@ void TextServerFallback::font_set_glyph_texture_idx(const RID &p_font_rid, const
}
RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, RID());
MutexLock lock(fd->mutex);
@@ -1764,8 +1784,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -1779,7 +1798,7 @@ RID TextServerFallback::font_get_glyph_texture_rid(const RID &p_font_rid, const
}
Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Size2());
MutexLock lock(fd->mutex);
@@ -1804,8 +1823,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -1819,7 +1837,7 @@ Size2 TextServerFallback::font_get_glyph_texture_size(const RID &p_font_rid, con
}
Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, int64_t p_size, int64_t p_index) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -1836,13 +1854,22 @@ Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, in
int error = FT_Load_Glyph(fd->cache[size]->face, FT_Get_Char_Index(fd->cache[size]->face, index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
ERR_FAIL_COND_V(error, Dictionary());
- double h = fd->cache[size]->ascent;
+ if (fd->embolden != 0.f) {
+ FT_Pos strength = fd->embolden * p_size * 4; // 26.6 fractional units (1 / 64).
+ FT_Outline_Embolden(&fd->cache[size]->face->glyph->outline, strength);
+ }
+
+ if (fd->transform != Transform2D()) {
+ FT_Matrix mat = { FT_Fixed(fd->transform[0][0] * 65536), FT_Fixed(fd->transform[0][1] * 65536), FT_Fixed(fd->transform[1][0] * 65536), FT_Fixed(fd->transform[1][1] * 65536) }; // 16.16 fractional units (1 / 65536).
+ FT_Outline_Transform(&fd->cache[size]->face->glyph->outline, &mat);
+ }
+
double scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
scale = scale * (double)p_size / (double)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, -fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
@@ -1860,7 +1887,7 @@ Dictionary TextServerFallback::font_get_glyph_contours(const RID &p_font_rid, in
}
Array TextServerFallback::font_get_kerning_list(const RID &p_font_rid, int64_t p_size) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1876,7 +1903,7 @@ Array TextServerFallback::font_get_kerning_list(const RID &p_font_rid, int64_t p
}
void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p_size) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1887,7 +1914,7 @@ void TextServerFallback::font_clear_kerning_map(const RID &p_font_rid, int64_t p
}
void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1898,7 +1925,7 @@ void TextServerFallback::font_remove_kerning(const RID &p_font_rid, int64_t p_si
}
void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1909,7 +1936,7 @@ void TextServerFallback::font_set_kerning(const RID &p_font_rid, int64_t p_size,
}
Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_size, const Vector2i &p_glyph_pair) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1917,7 +1944,7 @@ Vector2 TextServerFallback::font_get_kerning(const RID &p_font_rid, int64_t p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
- const HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> &kern = fd->cache[size]->kerning_map;
+ const HashMap<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
@@ -1949,7 +1976,7 @@ int64_t TextServerFallback::font_get_glyph_index(const RID &p_font_rid, int64_t
}
bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
ERR_FAIL_COND_V_MSG((p_char >= 0xd800 && p_char <= 0xdfff) || (p_char > 0x10ffff), false, "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_char, 16) + ".");
@@ -1957,7 +1984,7 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
}
- FontDataForSizeFallback *at_size = fd->cache.begin()->value;
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
#ifdef MODULE_FREETYPE_ENABLED
if (at_size && at_size->face) {
@@ -1968,14 +1995,14 @@ bool TextServerFallback::font_has_char(const RID &p_font_rid, int64_t p_char) co
}
String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
}
- FontDataForSizeFallback *at_size = fd->cache.begin()->value;
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
String chars;
#ifdef MODULE_FREETYPE_ENABLED
@@ -2001,7 +2028,7 @@ String TextServerFallback::font_get_supported_chars(const RID &p_font_rid) const
}
void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND_MSG((p_start >= 0xd800 && p_start <= 0xdfff) || (p_start > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_start, 16) + ".");
ERR_FAIL_COND_MSG((p_end >= 0xd800 && p_end <= 0xdfff) || (p_end > 0x10ffff), "Unicode parsing error: Invalid unicode codepoint " + String::num_int64(p_end, 16) + ".");
@@ -2034,7 +2061,7 @@ void TextServerFallback::font_render_range(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2063,7 +2090,7 @@ void TextServerFallback::font_render_glyph(const RID &p_font_rid, const Vector2i
}
void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2109,8 +2136,7 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2142,7 +2168,7 @@ void TextServerFallback::font_draw_glyph(const RID &p_font_rid, const RID &p_can
}
void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2188,8 +2214,7 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
img->generate_mipmaps();
}
if (tex.texture.is_null()) {
- tex.texture.instantiate();
- tex.texture->create_from_image(img);
+ tex.texture = ImageTexture::create_from_image(img);
} else {
tex.texture->update(img);
}
@@ -2221,7 +2246,7 @@ void TextServerFallback::font_draw_glyph_outline(const RID &p_font_rid, const RI
}
bool TextServerFallback::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2233,7 +2258,7 @@ bool TextServerFallback::font_is_language_supported(const RID &p_font_rid, const
}
void TextServerFallback::font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2241,7 +2266,7 @@ void TextServerFallback::font_set_language_support_override(const RID &p_font_ri
}
bool TextServerFallback::font_get_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2249,7 +2274,7 @@ bool TextServerFallback::font_get_language_support_override(const RID &p_font_ri
}
void TextServerFallback::font_remove_language_support_override(const RID &p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2257,7 +2282,7 @@ void TextServerFallback::font_remove_language_support_override(const RID &p_font
}
PackedStringArray TextServerFallback::font_get_language_support_overrides(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -2269,7 +2294,7 @@ PackedStringArray TextServerFallback::font_get_language_support_overrides(const
}
bool TextServerFallback::font_is_script_supported(const RID &p_font_rid, const String &p_script) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2281,7 +2306,7 @@ bool TextServerFallback::font_is_script_supported(const RID &p_font_rid, const S
}
void TextServerFallback::font_set_script_support_override(const RID &p_font_rid, const String &p_script, bool p_supported) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2289,7 +2314,7 @@ void TextServerFallback::font_set_script_support_override(const RID &p_font_rid,
}
bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2297,7 +2322,7 @@ bool TextServerFallback::font_get_script_support_override(const RID &p_font_rid,
}
void TextServerFallback::font_remove_script_support_override(const RID &p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2307,7 +2332,7 @@ void TextServerFallback::font_remove_script_support_override(const RID &p_font_r
}
PackedStringArray TextServerFallback::font_get_script_support_overrides(const RID &p_font_rid) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedStringArray());
MutexLock lock(fd->mutex);
@@ -2319,7 +2344,7 @@ PackedStringArray TextServerFallback::font_get_script_support_overrides(const RI
}
void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_rid, const Dictionary &p_overrides) {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2329,7 +2354,7 @@ void TextServerFallback::font_set_opentype_feature_overrides(const RID &p_font_r
}
Dictionary TextServerFallback::font_get_opentype_feature_overrides(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2341,7 +2366,7 @@ Dictionary TextServerFallback::font_supported_feature_list(const RID &p_font_rid
}
Dictionary TextServerFallback::font_supported_variation_list(const RID &p_font_rid) const {
- FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ FontFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2420,6 +2445,7 @@ void TextServerFallback::full_copy(ShapedTextDataFallback *p_shaped) {
RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
_THREAD_SAFE_METHOD_
+
ShapedTextDataFallback *sd = memnew(ShapedTextDataFallback);
sd->direction = p_direction;
sd->orientation = p_orientation;
@@ -2546,6 +2572,31 @@ bool TextServerFallback::shaped_text_get_preserve_control(const RID &p_shaped) c
return sd->preserve_control;
}
+void TextServerFallback::shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) {
+ ERR_FAIL_INDEX((int)p_spacing, 4);
+ ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->extra_spacing[p_spacing] != p_value) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->extra_spacing[p_spacing] = p_value;
+ invalidate(sd);
+ }
+}
+
+int64_t TextServerFallback::shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const {
+ ERR_FAIL_INDEX_V((int)p_spacing, 4, 0);
+
+ const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
+
+ MutexLock lock(sd->mutex);
+ return sd->extra_spacing[p_spacing];
+}
+
int64_t TextServerFallback::shaped_get_span_count(const RID &p_shaped) const {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0);
@@ -2798,6 +2849,8 @@ void TextServerFallback::_realign(ShapedTextDataFallback *p_sd) const {
}
RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
+ _THREAD_SAFE_METHOD_
+
const ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
@@ -2826,6 +2879,9 @@ RID TextServerFallback::shaped_text_substr(const RID &p_shaped, int64_t p_start,
new_sd->sort_valid = false;
new_sd->upos = sd->upos;
new_sd->uthk = sd->uthk;
+ for (int i = 0; i < TextServer::SPACING_MAX; i++) {
+ new_sd->extra_spacing[i] = sd->extra_spacing[i];
+ }
if (p_length > 0) {
new_sd->text = sd->text.substr(p_start - sd->start, p_length);
@@ -2894,7 +2950,7 @@ RID TextServerFallback::shaped_text_get_parent(const RID &p_shaped) const {
return sd->parent;
}
-double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags) {
+double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<JustificationFlag> p_jst_flags) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.0);
@@ -2909,7 +2965,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
int start_pos = 0;
int end_pos = sd->glyphs.size() - 1;
- if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_AFTER_LAST_TAB)) {
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
start = sd->glyphs.size() - 1;
@@ -2935,7 +2991,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
}
double justification_width;
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
if (sd->overrun_trim_data.trim_pos >= 0) {
end_pos = sd->overrun_trim_data.trim_pos;
justification_width = sd->width_trimmed;
@@ -2946,7 +3002,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
justification_width = sd->width;
}
- if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ if (p_jst_flags.has_flag(JUSTIFICATION_TRIM_EDGE_SPACES)) {
// Trim spaces.
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
@@ -2978,7 +3034,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
}
}
- if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ if ((space_count > 0) && p_jst_flags.has_flag(JUSTIFICATION_WORD_BOUND)) {
double delta_width_per_space = (p_width - justification_width) / space_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
@@ -2996,7 +3052,7 @@ double TextServerFallback::shaped_text_fit_to_width(const RID &p_shaped, double
sd->fit_width_minimum_reached = true;
}
- if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ if (!p_jst_flags.has_flag(JUSTIFICATION_CONSTRAIN_ELLIPSIS)) {
sd->width = justification_width;
}
@@ -3131,7 +3187,7 @@ bool TextServerFallback::shaped_text_update_justification_ops(const RID &p_shape
return true;
}
-void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, int64_t p_trim_flags) {
+void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_line, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) {
ShapedTextDataFallback *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
@@ -3143,14 +3199,14 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
- bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
- bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
- bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
- bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+ bool add_ellipsis = p_trim_flags.has_flag(OVERRUN_ADD_ELLIPSIS);
+ bool cut_per_word = p_trim_flags.has_flag(OVERRUN_TRIM_WORD_ONLY);
+ bool enforce_ellipsis = p_trim_flags.has_flag(OVERRUN_ENFORCE_ELLIPSIS);
+ bool justification_aware = p_trim_flags.has_flag(OVERRUN_JUSTIFICATION_AWARE);
Glyph *sd_glyphs = sd->glyphs.ptrw();
- if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ if (p_trim_flags.has_flag(OVERRUN_TRIM) || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
sd->overrun_trim_data.trim_pos = -1;
sd->overrun_trim_data.ellipsis_pos = -1;
return;
@@ -3203,7 +3259,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(const RID &p_shaped_l
int ellipsis_width = 0;
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
- ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ ellipsis_width = 3 * dot_adv.x + sd->extra_spacing[SPACING_GLYPH] + (cut_per_word ? whitespace_adv.x : 0);
}
int ell_min_characters = 6;
@@ -3405,10 +3461,13 @@ bool TextServerFallback::shaped_text_shape(const RID &p_shaped) {
sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
}
- if (font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE) && is_whitespace(sd->text[j - sd->start])) {
- gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_SPACE);
- } else {
- gl.advance += font_get_spacing(gl.font_rid, gl.font_size, TextServer::SPACING_GLYPH);
+ if (j < sd->end - 1) {
+ // Do not add extra spacing to the last glyph of the string.
+ if (is_whitespace(sd->text[j - sd->start])) {
+ gl.advance += sd->extra_spacing[SPACING_SPACE];
+ } else {
+ gl.advance += sd->extra_spacing[SPACING_GLYPH];
+ }
}
sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
@@ -3535,9 +3594,9 @@ Size2 TextServerFallback::shaped_text_get_size(const RID &p_shaped) const {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
- return Size2(sd->width, sd->ascent + sd->descent).ceil();
+ return Size2(sd->width, sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM]).ceil();
} else {
- return Size2(sd->ascent + sd->descent, sd->width).ceil();
+ return Size2(sd->ascent + sd->descent + sd->extra_spacing[SPACING_TOP] + sd->extra_spacing[SPACING_BOTTOM], sd->width).ceil();
}
}
@@ -3549,7 +3608,7 @@ double TextServerFallback::shaped_text_get_ascent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->ascent;
+ return sd->ascent + sd->extra_spacing[SPACING_TOP];
}
double TextServerFallback::shaped_text_get_descent(const RID &p_shaped) const {
@@ -3560,7 +3619,7 @@ double TextServerFallback::shaped_text_get_descent(const RID &p_shaped) const {
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->descent;
+ return sd->descent + sd->extra_spacing[SPACING_BOTTOM];
}
double TextServerFallback::shaped_text_get_width(const RID &p_shaped) const {
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 0d2fc2628d..497403afd7 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -67,8 +67,8 @@
#include <godot_cpp/classes/ref.hpp>
#include <godot_cpp/templates/hash_map.hpp>
+#include <godot_cpp/templates/hash_set.hpp>
#include <godot_cpp/templates/rid_owner.hpp>
-#include <godot_cpp/templates/set.hpp>
#include <godot_cpp/templates/thread_work_pool.hpp>
#include <godot_cpp/templates/vector.hpp>
@@ -144,7 +144,7 @@ class TextServerFallback : public TextServerExtension {
Vector2 advance;
};
- struct FontDataForSizeFallback {
+ struct FontForSizeFallback {
double ascent = 0.0;
double descent = 0.0;
double underline_position = 0.0;
@@ -152,21 +152,18 @@ class TextServerFallback : public TextServerExtension {
double scale = 1.0;
double oversampling = 1.0;
- int spacing_glyph = 0;
- int spacing_space = 0;
-
Vector2i size;
Vector<FontTexture> textures;
HashMap<int32_t, FontGlyph> glyph_map;
- HashMap<Vector2i, Vector2, VariantHasher, VariantComparator> kerning_map;
+ HashMap<Vector2i, Vector2> kerning_map;
#ifdef MODULE_FREETYPE_ENABLED
FT_Face face = nullptr;
FT_StreamRec stream;
#endif
- ~FontDataForSizeFallback() {
+ ~FontForSizeFallback() {
#ifdef MODULE_FREETYPE_ENABLED
if (face != nullptr) {
FT_Done_Face(face);
@@ -175,7 +172,7 @@ class TextServerFallback : public TextServerExtension {
}
};
- struct FontDataFallback {
+ struct FontFallback {
Mutex mutex;
bool antialiased = true;
@@ -192,11 +189,11 @@ class TextServerFallback : public TextServerExtension {
double embolden = 0.0;
Transform2D transform;
- uint32_t style_flags = 0;
+ BitField<TextServer::FontStyle> style_flags = 0;
String font_name;
String style_name;
- HashMap<Vector2i, FontDataForSizeFallback *, VariantHasher, VariantComparator> cache;
+ HashMap<Vector2i, FontForSizeFallback *, VariantHasher, VariantComparator> cache;
bool face_init = false;
Dictionary supported_varaitions;
@@ -209,31 +206,32 @@ class TextServerFallback : public TextServerExtension {
PackedByteArray data;
const uint8_t *data_ptr;
size_t data_size;
+ int face_index = 0;
mutable ThreadWorkPool work_pool;
- ~FontDataFallback() {
+ ~FontFallback() {
work_pool.finish();
- for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : cache) {
+ for (const KeyValue<Vector2i, FontForSizeFallback *> &E : cache) {
memdelete(E.value);
}
cache.clear();
}
};
- _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height, bool p_msdf) const;
#ifdef MODULE_MSDFGEN_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontFallback *p_font_data, FontForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
#endif
#ifdef MODULE_FREETYPE_ENABLED
- _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
#endif
- _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
- _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const;
- _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data);
+ _FORCE_INLINE_ bool _ensure_glyph(FontFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontFallback *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontFallback *p_font_data);
void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
- _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size(const FontFallback *p_font_data, int p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -243,7 +241,7 @@ class TextServerFallback : public TextServerExtension {
}
}
- _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontFallback *p_font_data, const Vector2i &p_size) const {
if (p_font_data->msdf) {
return Vector2i(p_font_data->msdf_source_size, 0);
} else if (p_font_data->fixed_size > 0) {
@@ -311,6 +309,7 @@ class TextServerFallback : public TextServerExtension {
double descent = 0.0; // Descent for horizontal layout, 1/2 of width for vertical.
double width = 0.0; // Width for horizontal layout, height for vertical.
double width_trimmed = 0.0;
+ int extra_spacing[4] = { 0, 0, 0, 0 };
double upos = 0.0;
double uthk = 0.0;
@@ -325,7 +324,7 @@ class TextServerFallback : public TextServerExtension {
// Common data.
double oversampling = 1.0;
- mutable RID_PtrOwner<FontDataFallback> font_owner;
+ mutable RID_PtrOwner<FontFallback> font_owner;
mutable RID_PtrOwner<ShapedTextDataFallback> shaped_owner;
void _realign(ShapedTextDataFallback *p_sd) const;
@@ -364,8 +363,13 @@ public:
virtual void font_set_data(const RID &p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(const RID &p_font_rid, const uint8_t *p_data_ptr, int64_t p_data_size) override;
- virtual void font_set_style(const RID &p_font_rid, int64_t /*FontStyle*/ p_style) override;
- virtual int64_t /*FontStyle*/ font_get_style(const RID &p_font_rid) const override;
+ virtual void font_set_face_index(const RID &p_font_rid, int64_t p_index) override;
+ virtual int64_t font_get_face_index(const RID &p_font_rid) const override;
+
+ virtual int64_t font_get_face_count(const RID &p_font_rid) const override;
+
+ virtual void font_set_style(const RID &p_font_rid, BitField<FontStyle> p_style) override;
+ virtual BitField<FontStyle> font_get_style(const RID &p_font_rid) const override;
virtual void font_set_style_name(const RID &p_font_rid, const String &p_name) override;
virtual String font_get_style_name(const RID &p_font_rid) const override;
@@ -431,9 +435,6 @@ public:
virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override;
virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override;
- virtual void font_set_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing, int64_t p_value) override;
- virtual int64_t font_get_spacing(const RID &p_font_rid, int64_t p_size, SpacingType p_spacing) const override;
-
virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override;
@@ -530,6 +531,9 @@ public:
virtual void shaped_text_set_preserve_control(const RID &p_shaped, bool p_enabled) override;
virtual bool shaped_text_get_preserve_control(const RID &p_shaped) const override;
+ virtual void shaped_text_set_spacing(const RID &p_shaped, SpacingType p_spacing, int64_t p_value) override;
+ virtual int64_t shaped_text_get_spacing(const RID &p_shaped, SpacingType p_spacing) const override;
+
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const Array &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
@@ -541,7 +545,7 @@ public:
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;
virtual RID shaped_text_get_parent(const RID &p_shaped) const override;
- virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, int64_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(const RID &p_shaped) override;
@@ -553,7 +557,7 @@ public:
virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override;
virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override;
- virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, int64_t p_trim_flags) override;
+ virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 77e370849f..c4462ba687 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -331,7 +331,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
Ref<Image> img;
img.instantiate();
img->create(w, h, false, Image::FORMAT_RGBA8);
- texture->create_from_image(img);
+ texture->set_image(img);
} else {
/* tear down the partial theora setup */
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 5fa6ace827..661fe343af 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -141,13 +141,14 @@ static int get_channel_count(Image::Format p_format) {
}
}
-Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale) {
+Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale) {
Image::Format format = p_img->get_format();
if (!is_supported_format(format)) {
// Format not supported
print_error("Image format not supported for saving as EXR. Consider saving as PNG.");
- return ERR_UNAVAILABLE;
+
+ return Vector<uint8_t>();
}
EXRHeader header;
@@ -175,15 +176,15 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
};
int channel_count = get_channel_count(format);
- ERR_FAIL_COND_V(channel_count < 0, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(p_grayscale && channel_count != 1, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(channel_count < 0, Vector<uint8_t>());
+ ERR_FAIL_COND_V(p_grayscale && channel_count != 1, Vector<uint8_t>());
int target_pixel_type = get_target_pixel_type(format);
- ERR_FAIL_COND_V(target_pixel_type < 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(target_pixel_type < 0, Vector<uint8_t>());
int target_pixel_type_size = get_pixel_type_size(target_pixel_type);
- ERR_FAIL_COND_V(target_pixel_type_size < 0, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(target_pixel_type_size < 0, Vector<uint8_t>());
SrcPixelType src_pixel_type = get_source_pixel_type(format);
- ERR_FAIL_COND_V(src_pixel_type == SRC_UNSUPPORTED, ERR_UNAVAILABLE);
+ ERR_FAIL_COND_V(src_pixel_type == SRC_UNSUPPORTED, Vector<uint8_t>());
const int pixel_count = p_img->get_width() * p_img->get_height();
const int *channel_mapping = channel_mappings[channel_count - 1];
@@ -270,15 +271,25 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
const char *err = nullptr;
size_t bytes = SaveEXRImageToMemory(&image, &header, &mem, &err);
+ if (err && *err != OK) {
+ return Vector<uint8_t>();
+ }
+ Vector<uint8_t> buffer;
+ buffer.resize(bytes);
+ memcpy(buffer.ptrw(), mem, bytes);
+ free(mem);
+ return buffer;
+}
- if (bytes == 0) {
- print_error(String("Saving EXR failed. Error: {0}").format(varray(err)));
+Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale) {
+ const Vector<uint8_t> buffer = save_exr_buffer(p_img, p_grayscale);
+ if (buffer.size() == 0) {
+ print_error(String("Saving EXR failed."));
return ERR_FILE_CANT_WRITE;
} else {
Ref<FileAccess> ref = FileAccess::open(p_path, FileAccess::WRITE);
ERR_FAIL_COND_V(ref.is_null(), ERR_FILE_CANT_WRITE);
- ref->store_buffer(mem, bytes);
- free(mem);
+ ref->store_buffer(buffer.ptr(), buffer.size());
}
return OK;
diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h
index 5bf95b265e..8f97f55408 100644
--- a/modules/tinyexr/image_saver_tinyexr.h
+++ b/modules/tinyexr/image_saver_tinyexr.h
@@ -34,5 +34,6 @@
#include "core/os/os.h"
Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
+Vector<uint8_t> save_exr_buffer(const Ref<Image> &p_img, bool p_grayscale);
#endif // IMAGE_SAVER_TINYEXR_H
diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp
index d49ac23fea..c5897f37c3 100644
--- a/modules/tinyexr/register_types.cpp
+++ b/modules/tinyexr/register_types.cpp
@@ -44,6 +44,7 @@ void initialize_tinyexr_module(ModuleInitializationLevel p_level) {
ImageLoader::add_image_format_loader(image_loader_tinyexr);
Image::save_exr_func = save_exr;
+ Image::save_exr_buffer_func = save_exr_buffer;
}
void uninitialize_tinyexr_module(ModuleInitializationLevel p_level) {
diff --git a/modules/visual_script/editor/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index 569bcad780..61ff56b62a 100644
--- a/modules/visual_script/editor/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -647,6 +647,14 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
Control::get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
};
+ // Visual script specific theme for MSDF font.
+ Ref<Theme> vstheme;
+ vstheme.instantiate();
+ Ref<Font> label_font = EditorNode::get_singleton()->get_editor_theme()->get_font("main_msdf", "EditorFonts");
+ vstheme->set_font("font", "Label", label_font);
+ vstheme->set_font("font", "LineEdit", label_font);
+ vstheme->set_font("font", "Button", label_font);
+
Ref<Texture2D> seq_port = Control::get_theme_icon(SNAME("VisualShaderPort"), SNAME("EditorIcons"));
List<int> node_ids;
script->get_node_list(&node_ids);
@@ -881,7 +889,49 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
} else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
- button->set_text(pi.hint_string.get_slice(",", value));
+ bool found = false;
+ const Vector<String> options = pi.hint_string.split(",");
+ int64_t current_val = 0;
+ for (const String &option : options) {
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != 1) {
+ current_val = text_split[1].to_int();
+ }
+ if (value.operator int() == current_val) {
+ button->set_text(text_split[0]);
+ found = true;
+ break;
+ }
+ current_val += 1;
+ }
+ if (!found) {
+ button->set_text(value);
+ }
+ } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_FLAGS) {
+ Vector<String> value_texts;
+ const Vector<String> options = pi.hint_string.split(",");
+ uint32_t v = value;
+ for (const String &option : options) {
+ uint32_t current_val;
+ Vector<String> text_split = option.split(":");
+ if (text_split.size() != -1) {
+ current_val = text_split[1].to_int();
+ } else {
+ current_val = 1 << i;
+ }
+ if ((v & current_val) == current_val) {
+ value_texts.push_back(text_split[0]);
+ }
+ }
+ if (value_texts.size() != 0) {
+ String value_text = value_texts[0];
+ for (const String &text : value_texts) {
+ value_text += " | " + text;
+ }
+ button->set_text(value_text);
+ } else {
+ button->set_text(value);
+ }
} else {
button->set_text(value);
}
@@ -960,9 +1010,8 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
slot_idx++;
}
-
graph->add_child(gnode);
-
+ gnode->set_theme(vstheme);
if (gnode->is_comment()) {
graph->move_child(gnode, 0);
}
@@ -973,6 +1022,9 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
+ float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature");
+ graph->set_connection_lines_curvature(graph_lines_curvature);
+
// Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything.
graph->call_deferred(SNAME("set_scroll_ofs"), script->get_scroll() * EDSCALE);
updating_graph = false;
@@ -1433,7 +1485,11 @@ void VisualScriptEditor::_deselect_input_names() {
}
}
-void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button) {
+void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button) {
+ if (p_mouse_button != MouseButton::LEFT) {
+ return;
+ }
+
TreeItem *ti = Object::cast_to<TreeItem>(p_item);
TreeItem *root = members->get_root();
@@ -1603,12 +1659,12 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
List<VisualScript::DataConnection> data_connections;
script->get_data_connection_list(&data_connections);
- HashMap<int, Set<int>> conn_map;
+ HashMap<int, RBSet<int>> conn_map;
for (const VisualScript::DataConnection &E : data_connections) {
if (E.from_node == p_id && E.from_port == p_port) {
// Push into the connections map.
if (!conn_map.has(E.to_node)) {
- conn_map.insert(E.to_node, Set<int>());
+ conn_map.insert(E.to_node, RBSet<int>());
}
conn_map[E.to_node].insert(E.to_port);
}
@@ -1617,9 +1673,9 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
undo_redo->add_do_method(vsn.ptr(), "remove_output_data_port", p_port);
undo_redo->add_do_method(this, "_update_graph", p_id);
- for (const KeyValue<int, Set<int>> &E : conn_map) {
- for (const Set<int>::Element *F = E.value.front(); F; F = F->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F->get());
+ for (const KeyValue<int, RBSet<int>> &E : conn_map) {
+ for (const int &F : E.value) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E.key, F);
}
}
@@ -1760,14 +1816,14 @@ void VisualScriptEditor::_on_nodes_paste() {
return;
}
- Map<int, int> remap;
+ HashMap<int, int> remap;
undo_redo->create_action(TTR("Paste VisualScript Nodes"));
int idc = script->get_available_id() + 1;
- Set<int> to_select;
+ RBSet<int> to_select;
- Set<Vector2> existing_positions;
+ RBSet<Vector2> existing_positions;
{
List<int> nodes;
@@ -1806,14 +1862,14 @@ void VisualScriptEditor::_on_nodes_paste() {
undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
}
- for (Set<VisualScript::SequenceConnection>::Element *E = clipboard->sequence_connections.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
+ for (const VisualScript::SequenceConnection &E : clipboard->sequence_connections) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E.from_node], E.from_output, remap[E.to_node]);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", remap[E.from_node], E.from_output, remap[E.to_node]);
}
- for (Set<VisualScript::DataConnection>::Element *E = clipboard->data_connections.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "data_connect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
+ for (const VisualScript::DataConnection &E : clipboard->data_connections) {
+ undo_redo->add_do_method(script.ptr(), "data_connect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port);
}
undo_redo->add_do_method(this, "_update_graph");
@@ -1881,7 +1937,7 @@ void VisualScriptEditor::_on_nodes_delete() {
}
void VisualScriptEditor::_on_nodes_duplicate() {
- Set<int> to_duplicate;
+ RBSet<int> to_duplicate;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
@@ -1900,20 +1956,20 @@ void VisualScriptEditor::_on_nodes_duplicate() {
undo_redo->create_action(TTR("Duplicate VisualScript Nodes"));
int idc = script->get_available_id() + 1;
- Set<int> to_select;
+ RBSet<int> to_select;
HashMap<int, int> remap;
- for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) {
+ for (const int &F : to_duplicate) {
// Duplicate from the specific function but place it into the default func as it would lack the connections.
- Ref<VisualScriptNode> node = script->get_node(F->get());
+ Ref<VisualScriptNode> node = script->get_node(F);
Ref<VisualScriptNode> dupe = node->duplicate(true);
int new_id = idc++;
- remap.insert(F->get(), new_id);
+ remap.insert(F, new_id);
to_select.insert(new_id);
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20));
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F) + Vector2(20, 20));
undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
}
@@ -1959,15 +2015,6 @@ void VisualScriptEditor::_generic_search(Vector2 pos, bool node_centered) {
}
new_connect_node_select->select_from_visual_script(script, false); // do not reset text
-
- // Ensure that the dialog fits inside the graph.
- Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
- pos.x = pos.x > bounds.x ? bounds.x : pos.x;
- pos.y = pos.y > bounds.y ? bounds.y : pos.y;
-
- if (pos != Vector2()) {
- new_connect_node_select->set_position(pos);
- }
}
void VisualScriptEditor::input(const Ref<InputEvent> &p_event) {
@@ -3175,7 +3222,7 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro
}
}
-VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &visited_nodes) {
+VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_action_node, int p_port_action_output, RBSet<int> &visited_nodes) {
VisualScriptNode::TypeGuess tg;
tg.type = Variant::NIL;
@@ -3226,7 +3273,7 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac
}
void VisualScriptEditor::_port_action_menu(int p_option) {
- Set<int> vn;
+ RBSet<int> vn;
switch (p_option) {
case CREATE_CALL_SET_GET: {
@@ -3338,7 +3385,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
#endif
Vector2 pos = _get_pos_in_graph(port_action_pos);
- Set<int> vn;
+ RBSet<int> vn;
bool port_node_exists = true;
if (drop_position != Vector2()) {
@@ -3928,6 +3975,9 @@ void VisualScriptEditor::_notification(int p_what) {
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning")));
graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning")));
+ graph->set_minimap_opacity(EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity"));
+ graph->set_connection_lines_curvature(EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature"));
+ _update_graph();
} break;
case NOTIFICATION_READY: {
@@ -4094,8 +4144,8 @@ void VisualScriptEditor::_menu_option(int p_what) {
} break;
case EDIT_CREATE_FUNCTION: {
// Create Function.
- Map<int, Ref<VisualScriptNode>> nodes;
- Set<int> selections;
+ HashMap<int, Ref<VisualScriptNode>> nodes;
+ RBSet<int> selections;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
@@ -4118,18 +4168,18 @@ void VisualScriptEditor::_menu_option(int p_what) {
return; // nothing to be done if there are no valid nodes selected
}
- Set<VisualScript::SequenceConnection> seqmove;
- Set<VisualScript::DataConnection> datamove;
+ RBSet<VisualScript::SequenceConnection> seqmove;
+ RBSet<VisualScript::DataConnection> datamove;
- Set<VisualScript::SequenceConnection> seqext;
- Set<VisualScript::DataConnection> dataext;
+ RBSet<VisualScript::SequenceConnection> seqext;
+ RBSet<VisualScript::DataConnection> dataext;
int start_node = -1;
- Set<int> end_nodes;
+ RBSet<int> end_nodes;
if (nodes.size() == 1) {
- Ref<VisualScriptNode> nd = script->get_node(nodes.front()->key());
+ Ref<VisualScriptNode> nd = script->get_node(nodes.begin()->key);
if (nd.is_valid() && nd->has_input_sequence_port()) {
- start_node = nodes.front()->key();
+ start_node = nodes.begin()->key;
} else {
EditorNode::get_singleton()->show_warning(TTR("Select at least one node with sequence port."));
return;
@@ -4167,8 +4217,8 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
} else {
// Pick the node with input sequence.
- Set<int> nodes_from;
- Set<int> nodes_to;
+ RBSet<int> nodes_from;
+ RBSet<int> nodes_to;
for (const VisualScript::SequenceConnection &E : seqs) {
if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
seqmove.insert(E);
@@ -4194,9 +4244,9 @@ void VisualScriptEditor::_menu_option(int p_what) {
// If we still don't have a start node then,
// run through the nodes and select the first tree node,
// i.e. node without any input sequence but output sequence.
- for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) {
- if (!nodes_to.has(E->get())) {
- start_node = E->get();
+ for (const int &E : nodes_from) {
+ if (!nodes_to.has(E)) {
+ start_node = E;
}
}
}
@@ -4265,13 +4315,13 @@ void VisualScriptEditor::_menu_option(int p_what) {
// Move the nodes.
// Handles reconnection of sequence connections on undo, start here in case of issues.
- for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E->get().from_node, E->get().from_output, E->get().to_node);
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node);
+ for (const VisualScript::SequenceConnection &E : seqext) {
+ undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E.from_node, E.from_output, E.to_node);
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
}
- for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "data_disconnect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ for (const VisualScript::DataConnection &E : dataext) {
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", E.from_node, E.from_port, E.to_node, E.to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
}
// I don't really think we need support for non sequenced functions at this moment.
@@ -4279,24 +4329,24 @@ void VisualScriptEditor::_menu_option(int p_what) {
// Could fail with the new changes, start here when searching for bugs in create function shortcut.
int m = 1;
- for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) {
+ for (const int &G : end_nodes) {
Ref<VisualScriptReturn> ret_node;
ret_node.instantiate();
int ret_id = fn_id + (m++);
selections.insert(ret_id);
- Vector2 posi = _get_available_pos(false, script->get_node_position(G->get()) + Vector2(80, -100));
+ Vector2 posi = _get_available_pos(false, script->get_node_position(G) + Vector2(80, -100));
undo_redo->add_do_method(script.ptr(), "add_node", ret_id, ret_node, posi);
undo_redo->add_undo_method(script.ptr(), "remove_node", ret_id);
- undo_redo->add_do_method(script.ptr(), "sequence_connect", G->get(), 0, ret_id);
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", G, 0, ret_id);
// Add data outputs from each of the end_nodes.
- Ref<VisualScriptNode> vsn = script->get_node(G->get());
+ Ref<VisualScriptNode> vsn = script->get_node(G);
if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) {
ret_node->set_enable_return_value(true);
// Use the zeroth data port cause that's the likely one that is planned to be used.
ret_node->set_return_type(vsn->get_output_value_port_info(0).type);
- undo_redo->add_do_method(script.ptr(), "data_connect", G->get(), 0, ret_id, 0);
+ undo_redo->add_do_method(script.ptr(), "data_connect", G, 0, ret_id, 0);
}
}
@@ -4331,7 +4381,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
// This is likely going to be very slow and I am not sure if I should keep it,
// but I hope that it will not be a problem considering that we won't be creating functions so frequently,
// and cyclic connections would be a problem but hopefully we won't let them get to this point.
-void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) {
+void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const RBSet<int> &p_selected, RBSet<int> &r_end_nodes) {
for (const VisualScript::SequenceConnection &E : p_seqs) {
int from = E.from_node;
int to = E.to_node;
@@ -4345,7 +4395,11 @@ void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::Sequence
}
}
-void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
+void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos, MouseButton p_button) {
+ if (p_button != MouseButton::RIGHT) {
+ return;
+ }
+
TreeItem *ti = members->get_selected();
ERR_FAIL_COND(!ti);
@@ -4546,11 +4600,11 @@ VisualScriptEditor::VisualScriptEditor() {
members_section->add_margin_child(TTR("Members:"), members, true);
members->set_custom_minimum_size(Size2(0, 50 * EDSCALE));
members->set_hide_root(true);
- members->connect("button_pressed", callable_mp(this, &VisualScriptEditor::_member_button));
+ members->connect("button_clicked", callable_mp(this, &VisualScriptEditor::_member_button));
members->connect("item_edited", callable_mp(this, &VisualScriptEditor::_member_edited));
members->connect("cell_selected", callable_mp(this, &VisualScriptEditor::_member_selected), varray(), CONNECT_DEFERRED);
members->connect("gui_input", callable_mp(this, &VisualScriptEditor::_members_gui_input));
- members->connect("item_rmb_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected));
+ members->connect("item_mouse_selected", callable_mp(this, &VisualScriptEditor::_member_rmb_selected));
members->set_allow_rmb_select(true);
members->set_allow_reselect(true);
members->set_hide_folding(true);
@@ -4574,7 +4628,8 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+ graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ graph->set_show_zoom_label(true);
graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected));
graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move));
graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move));
@@ -4586,6 +4641,8 @@ VisualScriptEditor::VisualScriptEditor() {
graph->set_drag_forwarding(this);
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
+ float graph_lines_curvature = EditorSettings::get_singleton()->get("editors/visual_editors/lines_curvature");
+ graph->set_connection_lines_curvature(graph_lines_curvature);
graph->hide();
graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed));
@@ -4660,7 +4717,7 @@ VisualScriptEditor::VisualScriptEditor() {
function_create_dialog = memnew(ConfirmationDialog);
function_create_dialog->set_title(TTR("Create Function"));
function_create_dialog->add_child(function_vb);
- function_create_dialog->get_ok_button()->set_text(TTR("Create"));
+ function_create_dialog->set_ok_button_text(TTR("Create"));
function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
add_child(function_create_dialog);
@@ -4704,7 +4761,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
edit_signal_dialog = memnew(AcceptDialog);
- edit_signal_dialog->get_ok_button()->set_text(TTR("Close"));
+ edit_signal_dialog->set_ok_button_text(TTR("Close"));
add_child(edit_signal_dialog);
signal_editor = memnew(VisualScriptEditorSignalEdit);
@@ -4714,7 +4771,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_signal_edit->edit(signal_editor);
edit_variable_dialog = memnew(AcceptDialog);
- edit_variable_dialog->get_ok_button()->set_text(TTR("Close"));
+ edit_variable_dialog->set_ok_button_text(TTR("Close"));
add_child(edit_variable_dialog);
variable_editor = memnew(VisualScriptEditorVariableEdit);
@@ -4837,7 +4894,7 @@ Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &
}
VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr;
-Map<String, Ref<RefCounted>> VisualScriptCustomNodes::custom_nodes;
+HashMap<String, Ref<RefCounted>> VisualScriptCustomNodes::custom_nodes;
VisualScriptCustomNodes::VisualScriptCustomNodes() {
singleton = this;
diff --git a/modules/visual_script/editor/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index e63539ac5b..fd59d22cbe 100644
--- a/modules/visual_script/editor/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -142,9 +142,9 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String>> args;
};
- Map<StringName, Color> node_colors;
+ HashMap<StringName, Color> node_colors;
HashMap<StringName, Ref<StyleBox>> node_styles;
- Map<StringName, Variant::Type> base_type_map;
+ HashMap<StringName, Variant::Type> base_type_map;
void _update_graph_connections();
void _update_graph(int p_only_id = -1);
@@ -159,11 +159,11 @@ class VisualScriptEditor : public ScriptEditorBase {
String _validate_name(const String &p_name) const;
struct Clipboard {
- Map<int, Ref<VisualScriptNode>> nodes;
- Map<int, Vector2> nodes_positions;
+ HashMap<int, Ref<VisualScriptNode>> nodes;
+ HashMap<int, Vector2> nodes_positions;
- Set<VisualScript::SequenceConnection> sequence_connections;
- Set<VisualScript::DataConnection> data_connections;
+ RBSet<VisualScript::SequenceConnection> sequence_connections;
+ RBSet<VisualScript::DataConnection> data_connections;
};
static Clipboard *clipboard;
@@ -213,7 +213,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _end_node_move();
void _move_node(int p_id, const Vector2 &p_to);
- void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes);
+ void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const RBSet<int> &p_selected, RBSet<int> &r_end_nodes);
void _node_moved(Vector2 p_from, Vector2 p_to, int p_id);
void _remove_node(int p_id);
@@ -226,7 +226,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _update_available_nodes();
- void _member_button(Object *p_item, int p_column, int p_button);
+ void _member_button(Object *p_item, int p_column, int p_button, MouseButton p_mouse_button);
void _expression_text_changed(const String &p_text, int p_id);
void _add_input_port(int p_id);
@@ -287,9 +287,9 @@ class VisualScriptEditor : public ScriptEditorBase {
void _draw_color_over_button(Object *obj, Color p_color);
void _button_resource_previewed(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud);
- VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, Set<int> &p_visited_nodes);
+ VisualScriptNode::TypeGuess _guess_output_type(int p_port_action_node, int p_port_action_output, RBSet<int> &p_visited_nodes);
- void _member_rmb_selected(const Vector2 &p_pos);
+ void _member_rmb_selected(const Vector2 &p_pos, MouseButton p_button);
void _member_option(int p_option);
void _toggle_scripts_pressed();
@@ -359,7 +359,7 @@ protected:
static void _bind_methods();
static VisualScriptCustomNodes *singleton;
- static Map<String, Ref<RefCounted>> custom_nodes;
+ static HashMap<String, Ref<RefCounted>> custom_nodes;
static Ref<VisualScriptNode> create_node_custom(const String &p_name);
public:
diff --git a/modules/visual_script/editor/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index c8549ddb53..f98ce5bb26 100644
--- a/modules/visual_script/editor/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -182,7 +182,7 @@ void VisualScriptPropertySelector::select_method_from_base_type(const String &p_
void VisualScriptPropertySelector::select_from_base_type(const String &p_base, const String &p_base_script, bool p_virtuals_only, const bool p_connecting, bool clear_text) {
set_title(TTR("Select from base type"));
base_type = p_base;
- base_script = p_base_script.lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_base_script.trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
type = Variant::NIL;
connecting = p_connecting;
@@ -217,7 +217,7 @@ void VisualScriptPropertySelector::select_from_script(const Ref<Script> &p_scrip
ERR_FAIL_COND(p_script.is_null());
base_type = p_script->get_instance_base_type();
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
type = Variant::NIL;
script = p_script->get_instance_id();
connecting = p_connecting;
@@ -312,7 +312,7 @@ void VisualScriptPropertySelector::select_from_instance(Object *p_instance, cons
if (p_script == nullptr) {
base_script = "";
} else {
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
}
type = Variant::NIL;
@@ -346,7 +346,7 @@ void VisualScriptPropertySelector::select_from_visual_script(const Ref<Script> &
if (p_script == nullptr) {
base_script = "";
} else {
- base_script = p_script->get_path().lstrip("res://").quote(); // filepath to EditorHelp::get_doc_data().name
+ base_script = p_script->get_path().trim_prefix("res://").quote(); // filepath to EditorHelp::get_doc_data().name
}
type = Variant::NIL;
connecting = false;
@@ -521,10 +521,19 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() {
results_tree->connect("item_selected", callable_mp(this, &VisualScriptPropertySelector::_item_selected));
vbox->add_child(results_tree);
+ ScrollContainer *scroller = memnew(ScrollContainer);
+ scroller->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+ scroller->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ scroller->set_custom_minimum_size(Size2(600, 400) * EDSCALE);
+ vbox->add_child(scroller);
+
help_bit = memnew(EditorHelpBit);
- vbox->add_child(help_bit);
+ help_bit->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ help_bit->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ scroller->add_child(help_bit);
+
help_bit->connect("request_hide", callable_mp(this, &VisualScriptPropertySelector::_hide_requested));
- get_ok_button()->set_text(TTR("Open"));
+ set_ok_button_text(TTR("Open"));
get_ok_button()->set_disabled(true);
set_hide_on_ok(false);
}
@@ -726,7 +735,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes_init() {
combined_docs.insert(class_doc.name, class_doc);
}
}
- iterator_doc = combined_docs.front();
+ iterator_doc = combined_docs.begin();
return true;
}
@@ -787,7 +796,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_node_classes_build() {
}
bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
- DocData::ClassDoc &class_doc = iterator_doc->value();
+ DocData::ClassDoc &class_doc = iterator_doc->value;
if (
(!_is_class_disabled_by_feature_profile(class_doc.name) && !_is_class_disabled_by_scope(class_doc.name)) ||
_match_visual_script(class_doc)) {
@@ -909,13 +918,13 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_match_classes() {
}
}
- iterator_doc = iterator_doc->next();
+ ++iterator_doc;
return !iterator_doc;
}
bool VisualScriptPropertySelector::SearchRunner::_phase_class_items_init() {
results_tree->clear();
- iterator_match = matches.front();
+ iterator_match = matches.begin();
root_item = results_tree->create_item();
class_items.clear();
@@ -928,7 +937,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
return true;
}
- ClassMatch &match = iterator_match->value();
+ ClassMatch &match = iterator_match->value;
if (search_flags & SEARCH_SHOW_HIERARCHY) {
if (match.required()) {
@@ -940,12 +949,12 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_class_items() {
}
}
- iterator_match = iterator_match->next();
+ ++iterator_match;
return !iterator_match;
}
bool VisualScriptPropertySelector::SearchRunner::_phase_member_items_init() {
- iterator_match = matches.front();
+ iterator_match = matches.begin();
return true;
}
@@ -955,7 +964,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
return true;
}
- ClassMatch &match = iterator_match->value();
+ ClassMatch &match = iterator_match->value;
TreeItem *parent = (search_flags & SEARCH_SHOW_HIERARCHY) ? class_items[match.doc->name] : root_item;
bool constructor_created = false;
@@ -986,7 +995,7 @@ bool VisualScriptPropertySelector::SearchRunner::_phase_member_items() {
_create_theme_property_item(parent, match.doc, match.theme_properties[i]);
}
- iterator_match = iterator_match->next();
+ ++iterator_match;
return !iterator_match;
}
diff --git a/modules/visual_script/editor/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 90a6265ab7..91d81bba47 100644
--- a/modules/visual_script/editor/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -179,15 +179,15 @@ class VisualScriptPropertySelector::SearchRunner : public RefCounted {
Ref<Texture2D> empty_icon;
Color disabled_color;
- Map<String, DocData::ClassDoc>::Element *iterator_doc = nullptr;
- Map<String, ClassMatch> matches;
- Map<String, ClassMatch>::Element *iterator_match = nullptr;
+ HashMap<String, DocData::ClassDoc>::Iterator iterator_doc;
+ HashMap<String, ClassMatch> matches;
+ HashMap<String, ClassMatch>::Iterator iterator_match;
TreeItem *root_item = nullptr;
- Map<String, TreeItem *> class_items;
+ HashMap<String, TreeItem *> class_items;
TreeItem *matched_item = nullptr;
float match_highest_score = 0;
- Map<String, DocData::ClassDoc> combined_docs;
+ HashMap<String, DocData::ClassDoc> combined_docs;
List<String> vs_nodes;
bool _is_class_disabled_by_feature_profile(const StringName &p_class);
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index c140ceb0b4..742fa75bb7 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -230,12 +230,12 @@ void VisualScript::_node_ports_changed(int p_id) {
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id && E->get().from_output >= vsn->get_output_sequence_port_count()) {
- to_remove.push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ if (E.from_node == p_id && E.from_output >= vsn->get_output_sequence_port_count()) {
+ to_remove.push_back(E);
}
- if (E->get().to_node == p_id && !vsn->has_input_sequence_port()) {
- to_remove.push_back(E->get());
+ if (E.to_node == p_id && !vsn->has_input_sequence_port()) {
+ to_remove.push_back(E);
}
}
@@ -248,12 +248,12 @@ void VisualScript::_node_ports_changed(int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id && E->get().from_port >= vsn->get_output_value_port_count()) {
- to_remove.push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ if (E.from_node == p_id && E.from_port >= vsn->get_output_value_port_count()) {
+ to_remove.push_back(E);
}
- if (E->get().to_node == p_id && E->get().to_port >= vsn->get_input_value_port_count()) {
- to_remove.push_back(E->get());
+ if (E.to_node == p_id && E.to_port >= vsn->get_input_value_port_count()) {
+ to_remove.push_back(E);
}
}
@@ -292,9 +292,9 @@ void VisualScript::remove_node(int p_id) {
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- to_remove.push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ to_remove.push_back(E);
}
}
@@ -307,9 +307,9 @@ void VisualScript::remove_node(int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- to_remove.push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ to_remove.push_back(E);
}
}
@@ -384,8 +384,8 @@ bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, i
}
void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const {
- for (const Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- r_connection->push_back(E->get());
+ for (const SequenceConnection &E : sequence_connections) {
+ r_connection->push_back(E);
}
}
@@ -426,8 +426,8 @@ bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_t
}
bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().to_node == p_node && E->get().to_port == p_port) {
+ for (const DataConnection &E : data_connections) {
+ if (E.to_node == p_node && E.to_port == p_port) {
return true;
}
}
@@ -435,10 +435,10 @@ bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
}
bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().to_node == p_node && E->get().to_port == p_port) {
- *r_node = E->get().from_node;
- *r_port = E->get().from_port;
+ for (const DataConnection &E : data_connections) {
+ if (E.to_node == p_node && E.to_port == p_port) {
+ *r_node = E.from_node;
+ *r_port = E.from_port;
return true;
}
}
@@ -446,8 +446,8 @@ bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port
}
void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const {
- for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- r_connection->push_back(E->get());
+ for (const DataConnection &E : data_connections) {
+ r_connection->push_back(E);
}
}
@@ -751,7 +751,7 @@ void VisualScript::_update_placeholders() {
return; // No bother if no placeholders.
}
List<PropertyInfo> pinfo;
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
for (const KeyValue<StringName, Variable> &E : variables) {
if (!variables[E.key]._export) {
@@ -764,8 +764,8 @@ void VisualScript::_update_placeholders() {
values[p.name] = variables[E.key].default_value;
}
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(pinfo, values);
+ for (PlaceHolderScriptInstance *E : placeholders) {
+ E->update(pinfo, values);
}
}
@@ -779,7 +779,7 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) {
placeholders.insert(sins);
List<PropertyInfo> pinfo;
- Map<StringName, Variant> values;
+ HashMap<StringName, Variant> values;
for (const KeyValue<StringName, Variable> &E : variables) {
if (!variables[E.key]._export) {
@@ -1078,19 +1078,19 @@ Dictionary VisualScript::_get_data() const {
d["nodes"] = nds;
Array seqconns;
- for (const Set<SequenceConnection>::Element *F = sequence_connections.front(); F; F = F->next()) {
- seqconns.push_back(F->get().from_node);
- seqconns.push_back(F->get().from_output);
- seqconns.push_back(F->get().to_node);
+ for (const SequenceConnection &F : sequence_connections) {
+ seqconns.push_back(F.from_node);
+ seqconns.push_back(F.from_output);
+ seqconns.push_back(F.to_node);
}
d["sequence_connections"] = seqconns;
Array dataconns;
- for (const Set<DataConnection>::Element *F = data_connections.front(); F; F = F->next()) {
- dataconns.push_back(F->get().from_node);
- dataconns.push_back(F->get().from_port);
- dataconns.push_back(F->get().to_node);
- dataconns.push_back(F->get().to_port);
+ for (const DataConnection &F : data_connections) {
+ dataconns.push_back(F.from_node);
+ dataconns.push_back(F.from_port);
+ dataconns.push_back(F.to_node);
+ dataconns.push_back(F.to_port);
}
d["data_connections"] = dataconns;
@@ -1168,10 +1168,10 @@ bool VisualScript::inherits_script(const Ref<Script> &p_script) const {
return this == p_script.ptr(); // There is no inheritance in visual scripts, so this is enough.
}
-Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
+RBSet<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>);
get_sequence_connection_list(sc);
- Set<int> connected;
+ RBSet<int> connected;
for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) {
if (E->get().from_node == from_node) {
connected.insert(E->get().from_output);
@@ -1191,23 +1191,23 @@ VisualScript::~VisualScript() {
////////////////////////////////////////////
bool VisualScriptInstance::set(const StringName &p_name, const Variant &p_value) {
- Map<StringName, Variant>::Element *E = variables.find(p_name);
+ HashMap<StringName, Variant>::Iterator E = variables.find(p_name);
if (!E) {
return false;
}
- E->get() = p_value;
+ E->value = p_value;
return true;
}
bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
- const Map<StringName, Variant>::Element *E = variables.find(p_name);
+ HashMap<StringName, Variant>::ConstIterator E = variables.find(p_name);
if (!E) {
return false;
}
- r_ret = E->get();
+ r_ret = E->value;
return true;
}
@@ -1315,9 +1315,9 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int
}
Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p_stack, int p_stack_size, VisualScriptNodeInstance *p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield, Callable::CallError &r_error) {
- Map<StringName, Function>::Element *F = functions.find(p_method);
+ HashMap<StringName, Function>::Iterator F = functions.find(p_method);
ERR_FAIL_COND_V(!F, Variant());
- Function *f = &F->get();
+ Function *f = &F->value;
// This call goes separate, so it can be yielded and suspended.
Variant *variant_stack = (Variant *)p_stack;
@@ -1528,7 +1528,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// If no exit bit was set, and has sequence outputs, guess next node.
if (output >= node->sequence_output_count) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Node returned an invalid sequence output: ") + itos(output);
+ error_str = RTR("Node returned an invalid sequence output:") + " " + itos(output);
error = true;
break;
}
@@ -1582,7 +1582,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
if (!found) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Found sequence bit but not the node in the stack, report bug!");
+ error_str = RTR("Found sequence bit but not the node in the stack (please report).");
error = true;
break;
}
@@ -1594,7 +1594,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// Check for stack overflow.
if (flow_stack_pos + 1 >= flow_max) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- error_str = RTR("Stack overflow with stack depth: ") + itos(output);
+ error_str = vformat(RTR("Stack overflow (stack size: %s). Check for infinite recursion in your script."), output);
error = true;
break;
}
@@ -1655,6 +1655,8 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
error_str += "Expected " + itos(r_error.argument) + " arguments.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
error_str += "Invalid Call.";
+ } else if (r_error.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ error_str += "Method not const in a const instance.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
error_str += "Base Instance is null";
}
@@ -1689,7 +1691,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
r_error.error = Callable::CallError::CALL_OK; //ok by default
- Map<StringName, Function>::Element *F = functions.find(p_method);
+ HashMap<StringName, Function>::Iterator F = functions.find(p_method);
if (!F) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
@@ -1697,7 +1699,7 @@ Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **
VSDEBUG("CALLING: " + String(p_method));
- Function *f = &F->get();
+ Function *f = &F->value;
int total_stack_size = 0;
@@ -1731,14 +1733,14 @@ Variant VisualScriptInstance::callp(const StringName &p_method, const Variant **
memset(pass_stack, 0, f->pass_stack_size * sizeof(int));
- Map<int, VisualScriptNodeInstance *>::Element *E = instances.find(f->node);
+ HashMap<int, VisualScriptNodeInstance *>::Iterator E = instances.find(f->node);
if (!E) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "No VisualScriptFunction node in function.");
}
- VisualScriptNodeInstance *node = E->get();
+ VisualScriptNodeInstance *node = E->value;
if (flow_stack) {
flow_stack[0] = node->get_id();
@@ -1839,7 +1841,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
function.pass_stack_size = 0;
function.node_count = 0;
- Map<StringName, int> local_var_indices;
+ HashMap<StringName, int> local_var_indices;
if (function.node < 0) {
VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E.key));
@@ -1861,31 +1863,31 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
max_input_args = MAX(max_input_args, function.argument_count);
}
// Function nodes graphs.
- Set<VisualScript::SequenceConnection> seqconns;
- Set<VisualScript::DataConnection> dataconns;
- Set<int> node_ids;
+ RBSet<VisualScript::SequenceConnection> seqconns;
+ RBSet<VisualScript::DataConnection> dataconns;
+ RBSet<int> node_ids;
node_ids.insert(function.node);
{
List<int> nd_queue;
nd_queue.push_back(function.node);
while (!nd_queue.is_empty()) {
- for (const Set<VisualScript::SequenceConnection>::Element *F = script->sequence_connections.front(); F; F = F->next()) {
- if (nd_queue.front()->get() == F->get().from_node && !node_ids.has(F->get().to_node)) {
- nd_queue.push_back(F->get().to_node);
- node_ids.insert(F->get().to_node);
+ for (const VisualScript::SequenceConnection &F : script->sequence_connections) {
+ if (nd_queue.front()->get() == F.from_node && !node_ids.has(F.to_node)) {
+ nd_queue.push_back(F.to_node);
+ node_ids.insert(F.to_node);
}
- if (nd_queue.front()->get() == F->get().from_node && !seqconns.has(F->get())) {
- seqconns.insert(F->get());
+ if (nd_queue.front()->get() == F.from_node && !seqconns.has(F)) {
+ seqconns.insert(F);
}
}
nd_queue.pop_front();
}
HashMap<int, HashMap<int, Pair<int, int>>> dc_lut; // :: to -> to_port -> (from, from_port)
- for (const Set<VisualScript::DataConnection>::Element *F = script->data_connections.front(); F; F = F->next()) {
- dc_lut[F->get().to_node][F->get().to_port] = Pair<int, int>(F->get().from_node, F->get().from_port);
+ for (const VisualScript::DataConnection &F : script->data_connections) {
+ dc_lut[F.to_node][F.to_port] = Pair<int, int>(F.from_node, F.from_port);
}
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- nd_queue.push_back(F->get());
+ for (const int &F : node_ids) {
+ nd_queue.push_back(F);
}
List<int> dc_keys;
while (!nd_queue.is_empty()) {
@@ -1907,15 +1909,15 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
//Multiple passes are required to set up this complex thing..
//First create the nodes.
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- Ref<VisualScriptNode> node = script->nodes[F->get()].node;
+ for (const int &F : node_ids) {
+ Ref<VisualScriptNode> node = script->nodes[F].node;
VisualScriptNodeInstance *instance = node->instantiate(this); // Create instance.
ERR_FAIL_COND(!instance);
instance->base = node.ptr();
- instance->id = F->get();
+ instance->id = F;
instance->input_port_count = node->get_input_value_port_count();
instance->input_ports = nullptr;
instance->output_port_count = node->get_output_value_port_count();
@@ -1975,14 +1977,14 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
max_input_args = MAX(max_input_args, instance->input_port_count);
max_output_args = MAX(max_output_args, instance->output_port_count);
- instances[F->get()] = instance;
+ instances[F] = instance;
}
function.trash_pos = function.max_stack++; // create pos for trash
// Second pass, do data connections.
- for (const Set<VisualScript::DataConnection>::Element *F = dataconns.front(); F; F = F->next()) {
- VisualScript::DataConnection dc = F->get();
+ for (const VisualScript::DataConnection &F : dataconns) {
+ VisualScript::DataConnection dc = F;
ERR_CONTINUE(!instances.has(dc.from_node));
VisualScriptNodeInstance *from = instances[dc.from_node];
ERR_CONTINUE(!instances.has(dc.to_node));
@@ -2008,8 +2010,8 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
}
// Third pass, do sequence connections.
- for (const Set<VisualScript::SequenceConnection>::Element *F = seqconns.front(); F; F = F->next()) {
- VisualScript::SequenceConnection sc = F->get();
+ for (const VisualScript::SequenceConnection &F : seqconns) {
+ VisualScript::SequenceConnection sc = F;
ERR_CONTINUE(!instances.has(sc.from_node));
VisualScriptNodeInstance *from = instances[sc.from_node];
ERR_CONTINUE(!instances.has(sc.to_node));
@@ -2022,11 +2024,11 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
//fourth pass:
// 1) unassigned input ports to default values
// 2) connect unassigned output ports to trash
- for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
- ERR_CONTINUE(!instances.has(F->get()));
+ for (const int &F : node_ids) {
+ ERR_CONTINUE(!instances.has(F));
- Ref<VisualScriptNode> node = script->nodes[F->get()].node;
- VisualScriptNodeInstance *instance = instances[F->get()];
+ Ref<VisualScriptNode> node = script->nodes[F].node;
+ VisualScriptNodeInstance *instance = instances[F];
// Connect to default values.
for (int i = 0; i < instance->input_port_count; i++) {
@@ -2223,7 +2225,7 @@ Ref<Script> VisualScriptLanguage::make_template(const String &p_template, const
return script;
}
-bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const {
return false;
}
@@ -2433,6 +2435,9 @@ void VisualScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) c
void VisualScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const {
}
+void VisualScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
+}
+
void VisualScriptLanguage::profiling_start() {
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 6b27af15f6..716310f59b 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -36,6 +36,7 @@
#include "core/doc_data.h"
#include "core/object/script_language.h"
#include "core/os/thread.h"
+#include "core/templates/rb_set.h"
class VisualScriptInstance;
class VisualScriptNodeInstance;
@@ -214,8 +215,8 @@ private:
HashMap<int, NodeData> nodes; // Can be a sparse map.
- Set<SequenceConnection> sequence_connections;
- Set<DataConnection> data_connections;
+ RBSet<SequenceConnection> sequence_connections;
+ RBSet<DataConnection> data_connections;
Vector2 scroll;
@@ -233,15 +234,15 @@ private:
HashMap<StringName, Function> functions;
HashMap<StringName, Variable> variables;
- Map<StringName, Vector<Argument>> custom_signals;
+ HashMap<StringName, Vector<Argument>> custom_signals;
Vector<Multiplayer::RPCConfig> rpc_functions;
- Map<Object *, VisualScriptInstance *> instances;
+ HashMap<Object *, VisualScriptInstance *> instances;
bool is_tool_script;
#ifdef TOOLS_ENABLED
- Set<PlaceHolderScriptInstance *> placeholders;
+ RBSet<PlaceHolderScriptInstance *> placeholders;
// void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
void _update_placeholders();
@@ -283,7 +284,7 @@ public:
void sequence_disconnect(int p_from_node, int p_from_output, int p_to_node);
bool has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const;
void get_sequence_connection_list(List<SequenceConnection> *r_connection) const;
- Set<int> get_output_sequence_ports_connected(int from_node);
+ RBSet<int> get_output_sequence_ports_connected(int from_node);
void data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
@@ -317,7 +318,7 @@ public:
void custom_signal_swap_argument(const StringName &p_func, int p_argidx, int p_with_argidx);
void remove_custom_signal(const StringName &p_name);
void rename_custom_signal(const StringName &p_name, const StringName &p_new_name);
- Set<int> get_output_sequence_ports_connected(const String &edited_func, int from_node);
+ RBSet<int> get_output_sequence_ports_connected(const String &edited_func, int from_node);
void get_custom_signal_list(List<StringName> *r_custom_signals) const;
@@ -376,8 +377,8 @@ class VisualScriptInstance : public ScriptInstance {
Object *owner = nullptr;
Ref<VisualScript> script;
- Map<StringName, Variant> variables; // Using variable path, not script.
- Map<int, VisualScriptNodeInstance *> instances;
+ HashMap<StringName, Variant> variables; // Using variable path, not script.
+ HashMap<int, VisualScriptNodeInstance *> instances;
struct Function {
int node = 0;
@@ -389,7 +390,7 @@ class VisualScriptInstance : public ScriptInstance {
int argument_count = 0;
};
- Map<StringName, Function> functions;
+ HashMap<StringName, Function> functions;
Vector<Variant> default_values;
int max_input_args = 0;
@@ -415,22 +416,22 @@ public:
String to_string(bool *r_valid);
bool set_variable(const StringName &p_variable, const Variant &p_value) {
- Map<StringName, Variant>::Element *E = variables.find(p_variable);
+ HashMap<StringName, Variant>::Iterator E = variables.find(p_variable);
if (!E) {
return false;
}
- E->get() = p_value;
+ E->value = p_value;
return true;
}
bool get_variable(const StringName &p_variable, Variant *r_variable) const {
- const Map<StringName, Variant>::Element *E = variables.find(p_variable);
+ HashMap<StringName, Variant>::ConstIterator E = variables.find(p_variable);
if (!E) {
return false;
}
- *r_variable = E->get();
+ *r_variable = E->value;
return true;
}
@@ -480,7 +481,7 @@ public:
typedef Ref<VisualScriptNode> (*VisualScriptNodeRegisterFunc)(const String &p_type);
class VisualScriptLanguage : public ScriptLanguage {
- Map<String, VisualScriptNodeRegisterFunc> register_funcs;
+ HashMap<String, VisualScriptNodeRegisterFunc> register_funcs;
struct CallLevel {
Variant *stack = nullptr;
@@ -521,7 +522,7 @@ public:
if (_debug_call_stack_pos >= _debug_max_call_stack) {
// Stack overflow.
- _debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
+ _debug_error = vformat("Stack overflow (stack size: %s). Check for infinite recursion in your script.", _debug_max_call_stack);
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -544,7 +545,7 @@ public:
}
if (_debug_call_stack_pos == 0) {
- _debug_error = "Stack Underflow (Engine Bug)";
+ _debug_error = "Stack underflow (engine bug), please report.";
EngineDebugger::get_script_debugger()->debug(this);
return;
}
@@ -570,7 +571,7 @@ public:
virtual void get_string_delimiters(List<String> *p_delimiters) const override;
virtual bool is_using_templates() override;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override;
- virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override;
virtual Script *create_script() const override;
virtual bool has_named_classes() const override;
virtual bool supports_builtin_mode() const override;
@@ -598,6 +599,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
+ virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override;
virtual void profiling_stop() override;
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 0e63753720..19bbd834cc 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -391,7 +391,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Input type not iterable: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Input type not iterable:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
@@ -414,7 +414,7 @@ public:
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("Iterator became invalid: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = RTR("Iterator became invalid:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
return 0;
}
@@ -803,7 +803,7 @@ public:
//if the script is not in use by anyone, we can safely assume whatever we got is not casting to it.
return 1;
}
- Ref<Script> cast_script = Ref<Resource>(ResourceCache::get(script));
+ Ref<Script> cast_script = ResourceCache::get_ref(script);
if (!cast_script.is_valid()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_error_str = "Script path is not a script: " + script;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index 483fc8b6c3..b16358ae38 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -377,7 +377,7 @@ void VisualScriptFunctionCall::_update_method_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -587,7 +587,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_METHOD_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
@@ -1178,7 +1178,7 @@ void VisualScriptPropertySet::_update_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -1338,7 +1338,7 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
@@ -1864,7 +1864,7 @@ void VisualScriptPropertyGet::_update_cache() {
}
if (ResourceCache::has(base_script)) {
- script = Ref<Resource>(ResourceCache::get(base_script));
+ script = ResourceCache::get_ref(base_script);
} else {
return;
}
@@ -2044,7 +2044,7 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
}
if (ResourceCache::has(base_script)) {
- Ref<Script> script = Ref<Resource>(ResourceCache::get(base_script));
+ Ref<Script> script = ResourceCache::get_ref(base_script);
if (script.is_valid()) {
property.hint = PROPERTY_HINT_PROPERTY_OF_SCRIPT;
property.hint_string = itos(script->get_instance_id());
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index dbbe74f3d5..2dfc6da181 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -1116,9 +1116,9 @@ public:
r_error_str = *p_outputs[0];
} else {
if (unary) {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid argument of type:") + " " + Variant::get_type_name(p_inputs[0]->get_type());
} else {
- r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + ": " + RTR("Invalid arguments:") + " A: " + Variant::get_type_name(p_inputs[0]->get_type()) + ", B: " + Variant::get_type_name(p_inputs[1]->get_type());
}
}
}
@@ -1335,7 +1335,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
if (!instance->get_variable(variable, p_outputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableGet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableGet not found in script:") + " '" + String(variable) + "'";
return 0;
}
return 0;
@@ -1447,7 +1447,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) override {
if (!instance->set_variable(variable, *p_inputs[0])) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_error_str = RTR("VariableSet not found in script: ") + "'" + String(variable) + "'";
+ r_error_str = RTR("VariableSet not found in script:") + " '" + String(variable) + "'";
}
return 0;
@@ -3416,7 +3416,7 @@ VisualScriptConstructor::VisualScriptConstructor() {
type = Variant::NIL;
}
-static Map<String, Pair<Variant::Type, MethodInfo>> constructor_map;
+static HashMap<String, Pair<Variant::Type, MethodInfo>> constructor_map;
static Ref<VisualScriptNode> create_constructor_node(const String &p_name) {
ERR_FAIL_COND_V(!constructor_map.has(p_name), Ref<VisualScriptNode>());
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index b645a48c88..89a6b03ff8 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -36,23 +36,22 @@
int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, 0);
- ERR_FAIL_COND_V(!active, 0);
+
+ if (!active) {
+ return 0;
+ }
int todo = p_frames;
int start_buffer = 0;
- int frames_mixed_this_step = p_frames;
-
- while (todo && active) {
+ while (todo > 0 && active) {
AudioFrame *buffer = p_buffer;
if (start_buffer > 0) {
buffer = buffer + start_buffer;
}
int mixed = _mix_frames_vorbis(buffer, todo);
- if (mixed < 0) {
- return 0;
- }
+ ERR_FAIL_COND_V(mixed < 0, 0);
todo -= mixed;
frames_mixed += mixed;
start_buffer += mixed;
@@ -67,16 +66,14 @@ int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
// we still have buffer to fill, start from this element in the next iteration.
start_buffer = p_frames - todo;
} else {
- frames_mixed_this_step = p_frames - todo;
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
active = false;
- todo = 0;
}
}
}
- return frames_mixed_this_step;
+ return p_frames - todo;
}
int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 03e145216a..7ee6446313 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -57,7 +57,7 @@ String ResourceImporterOGGVorbis::get_resource_type() const {
return "AudioStreamOGGVorbis";
}
-bool ResourceImporterOGGVorbis::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const {
+bool ResourceImporterOGGVorbis::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const {
return true;
}
@@ -74,7 +74,7 @@ void ResourceImporterOGGVorbis::get_import_options(const String &p_path, List<Im
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
}
-Error ResourceImporterOGGVorbis::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, Variant *r_metadata) {
+Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
bool loop = p_options["loop"];
float loop_offset = p_options["loop_offset"];
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 07291803a1..3b4a68a1fd 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -52,9 +52,9 @@ public:
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
- 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;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
ResourceImporterOGGVorbis();
};
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 0e41f6c973..778d562278 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -34,184 +34,21 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include "webp_common.h"
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
-static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- uint8_t *dst_buff = nullptr;
- size_t dst_size = 0;
- if (img->get_format() == Image::FORMAT_RGB8) {
- dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- } else {
- dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- }
-
- ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
- Vector<uint8_t> dst;
- dst.resize(4 + dst_size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], dst_buff, dst_size);
- WebPFree(dst_buff);
-
- return dst;
-}
-
-static Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
- compression_level = CLAMP(compression_level, 0, 9);
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- // we need to use the more complex API in order to access the 'exact' flag...
-
- WebPConfig config;
- WebPPicture pic;
- if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
- ERR_FAIL_V(Vector<uint8_t>());
- }
-
- WebPMemoryWriter wrt;
- config.exact = 1;
- pic.use_argb = 1;
- pic.width = s.width;
- pic.height = s.height;
- pic.writer = WebPMemoryWrite;
- pic.custom_ptr = &wrt;
- WebPMemoryWriterInit(&wrt);
-
- bool success_import = false;
- if (img->get_format() == Image::FORMAT_RGB8) {
- success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
- } else {
- success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
- }
- bool success_encode = false;
- if (success_import) {
- success_encode = WebPEncode(&config, &pic);
- }
- WebPPictureFree(&pic);
-
- if (!success_encode) {
- WebPMemoryWriterClear(&wrt);
- ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
- }
-
- // copy from wrt
- Vector<uint8_t> dst;
- dst.resize(4 + wrt.size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], wrt.mem, wrt.size);
- WebPMemoryWriterClear(&wrt);
-
- return dst;
-}
-
-static Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
- int size = p_buffer.size() - 4;
- ERR_FAIL_COND_V(size <= 0, Ref<Image>());
- const uint8_t *r = p_buffer.ptr();
-
- ERR_FAIL_COND_V(r[0] != 'W' || r[1] != 'E' || r[2] != 'B' || r[3] != 'P', Ref<Image>());
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(&r[4], size, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
- }
-
- /*
- print_line("width: "+itos(features.width));
- print_line("height: "+itos(features.height));
- print_line("alpha: "+itos(features.has_alpha));
- */
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
-
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(&r[4], size, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(&r[4], size, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
-
- Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
- return img;
-}
-
-Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
-
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V(ERR_FILE_CORRUPT);
- }
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
-
- p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
-
- return OK;
-}
-
static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
img.instantiate();
- Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
+ Error err = WebPCommon::webp_load_image_from_buffer(img.ptr(), p_png, p_size);
ERR_FAIL_COND_V(err, Ref<Image>());
return img;
}
-Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
@@ -221,18 +58,18 @@ Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_
f->get_buffer(&w[0], src_image_len);
- Error err = webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
+ Error err = WebPCommon::webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
return err;
}
-void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
+void ImageLoaderWebP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("webp");
}
-ImageLoaderWEBP::ImageLoaderWEBP() {
+ImageLoaderWebP::ImageLoaderWebP() {
Image::_webp_mem_loader_func = _webp_mem_loader_func;
- Image::webp_lossy_packer = _webp_lossy_pack;
- Image::webp_lossless_packer = _webp_lossless_pack;
- Image::webp_unpacker = _webp_unpack;
+ Image::webp_lossy_packer = WebPCommon::_webp_lossy_pack;
+ Image::webp_lossless_packer = WebPCommon::_webp_lossless_pack;
+ Image::webp_unpacker = WebPCommon::_webp_unpack;
}
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index 1acd1459a0..448f683eb3 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -33,11 +33,11 @@
#include "core/io/image_loader.h"
-class ImageLoaderWEBP : public ImageFormatLoader {
+class ImageLoaderWebP : public ImageFormatLoader {
public:
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
- ImageLoaderWEBP();
+ ImageLoaderWebP();
};
#endif
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 148e325498..29f633743e 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -31,16 +31,20 @@
#include "register_types.h"
#include "image_loader_webp.h"
+#include "resource_saver_webp.h"
-static ImageLoaderWEBP *image_loader_webp = nullptr;
+static ImageLoaderWebP *image_loader_webp = nullptr;
+static Ref<ResourceSaverWebP> resource_saver_webp;
void initialize_webp_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_webp = memnew(ImageLoaderWEBP);
+ image_loader_webp = memnew(ImageLoaderWebP);
+ resource_saver_webp.instantiate();
ImageLoader::add_image_format_loader(image_loader_webp);
+ ResourceSaver::add_resource_format_saver(resource_saver_webp);
}
void uninitialize_webp_module(ModuleInitializationLevel p_level) {
@@ -49,4 +53,6 @@ void uninitialize_webp_module(ModuleInitializationLevel p_level) {
}
memdelete(image_loader_webp);
+ ResourceSaver::remove_resource_format_saver(resource_saver_webp);
+ resource_saver_webp.unref();
}
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
new file mode 100644
index 0000000000..d270d39163
--- /dev/null
+++ b/modules/webp/resource_saver_webp.cpp
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* resource_saver_webp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_saver_webp.h"
+
+#include "core/io/file_access.h"
+#include "core/io/image.h"
+#include "scene/resources/texture.h"
+#include "webp_common.h"
+
+Error ResourceSaverWebP::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ Ref<ImageTexture> texture = p_resource;
+
+ ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP.");
+ ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP.");
+
+ Ref<Image> img = texture->get_image();
+
+ Error err = save_image(p_path, img);
+
+ return err;
+}
+
+Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality);
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path));
+
+ const uint8_t *reader = buffer.ptr();
+
+ file->store_buffer(reader, buffer.size());
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+Vector<uint8_t> ResourceSaverWebP::save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer;
+ if (p_lossy) {
+ buffer = WebPCommon::_webp_lossy_pack(p_img, p_quality);
+ } else {
+ buffer = WebPCommon::_webp_lossless_pack(p_img);
+ }
+ return buffer;
+}
+
+bool ResourceSaverWebP::recognize(const Ref<Resource> &p_resource) const {
+ return (p_resource.is_valid() && p_resource->is_class("ImageTexture"));
+}
+
+void ResourceSaverWebP::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ if (Object::cast_to<ImageTexture>(*p_resource)) {
+ p_extensions->push_back("webp");
+ }
+}
+
+ResourceSaverWebP::ResourceSaverWebP() {
+ Image::save_webp_func = &save_image;
+ Image::save_webp_buffer_func = &save_image_to_buffer;
+}
diff --git a/modules/webp/resource_saver_webp.h b/modules/webp/resource_saver_webp.h
new file mode 100644
index 0000000000..59e944efa0
--- /dev/null
+++ b/modules/webp/resource_saver_webp.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* resource_saver_webp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_SAVER_WEBP_H
+#define RESOURCE_SAVER_WEBP_H
+
+#include "core/io/image.h"
+#include "core/io/resource_saver.h"
+
+class ResourceSaverWebP : public ResourceFormatSaver {
+public:
+ static Error save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+ static Vector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+
+ virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+
+ ResourceSaverWebP();
+};
+
+#endif // RESOURCE_SAVER_WEBP_H
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
new file mode 100644
index 0000000000..8657a98853
--- /dev/null
+++ b/modules/webp/webp_common.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* webp_common.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "webp_common.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+
+#include <string.h>
+#include <webp/decode.h>
+#include <webp/encode.h>
+
+namespace WebPCommon {
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ uint8_t *dst_buff = nullptr;
+ size_t dst_size = 0;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ } else {
+ dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ }
+
+ ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
+ Vector<uint8_t> dst;
+ dst.resize(dst_size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, dst_buff, dst_size);
+ WebPFree(dst_buff);
+
+ return dst;
+}
+
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
+ compression_level = CLAMP(compression_level, 0, 9);
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ // we need to use the more complex API in order to access the 'exact' flag...
+
+ WebPConfig config;
+ WebPPicture pic;
+ if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
+ ERR_FAIL_V(Vector<uint8_t>());
+ }
+
+ WebPMemoryWriter wrt;
+ config.exact = 1;
+ pic.use_argb = 1;
+ pic.width = s.width;
+ pic.height = s.height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ bool success_import = false;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
+ } else {
+ success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
+ }
+ bool success_encode = false;
+ if (success_import) {
+ success_encode = WebPEncode(&config, &pic);
+ }
+ WebPPictureFree(&pic);
+
+ if (!success_encode) {
+ WebPMemoryWriterClear(&wrt);
+ ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
+ }
+
+ // copy from wrt
+ Vector<uint8_t> dst;
+ dst.resize(wrt.size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, wrt.mem, wrt.size);
+ WebPMemoryWriterClear(&wrt);
+ return dst;
+}
+
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
+ int size = p_buffer.size();
+ ERR_FAIL_COND_V(size <= 0, Ref<Image>());
+ const uint8_t *r = p_buffer.ptr();
+
+ // A WebP file uses a RIFF header, which starts with "RIFF____WEBP".
+ ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>());
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(r, size, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(r, size, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
+
+ Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
+ return img;
+}
+
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
+
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
+
+ p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
+
+ return OK;
+}
+} // namespace WebPCommon
diff --git a/modules/webp/webp_common.h b/modules/webp/webp_common.h
new file mode 100644
index 0000000000..11bef40256
--- /dev/null
+++ b/modules/webp/webp_common.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* webp_common.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WEBP_COMMON_H
+#define WEBP_COMMON_H
+
+#include "core/io/image.h"
+
+namespace WebPCommon {
+// Given an image, pack this data into a WebP file.
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality);
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image);
+// Given a WebP file, unpack it into an image.
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer);
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len);
+} //namespace WebPCommon
+
+#endif // WEBP_COMMON_H
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index b4d97077e3..fed67397d1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -95,6 +95,13 @@
Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
+ <method name="set_default_extension" qualifiers="static">
+ <return type="void" />
+ <argument index="0" name="extension_class" type="StringName" />
+ <description>
+ Sets the [code]extension_class[/code] as the default [WebRTCPeerConnectionExtension] returned when creating a new [WebRTCPeerConnection].
+ </description>
+ </method>
<method name="set_local_description">
<return type="int" enum="Error" />
<argument index="0" name="type" type="String" />
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
index e88acdc845..163d939ac1 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -62,10 +62,5 @@
<description>
</description>
</method>
- <method name="make_default">
- <return type="void" />
- <description>
- </description>
- </method>
</methods>
</class>
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index eac8f85a84..75e29283ec 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -33,7 +33,7 @@
#include "core/io/packet_peer.h"
-#define WRTC_IN_BUF "network/limits/webrtc/max_channel_in_buffer_kb"
+#define WRTC_IN_BUF PNAME("network/limits/webrtc/max_channel_in_buffer_kb")
class WebRTCDataChannel : public PacketPeer {
GDCLASS(WebRTCDataChannel, PacketPeer);
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index 0bc42b104c..6f68b84ad3 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -140,41 +140,41 @@ void WebRTCMultiplayerPeer::poll() {
}
void WebRTCMultiplayerPeer::_find_next_peer() {
- Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(next_packet_peer);
+ HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(next_packet_peer);
if (E) {
- E = E->next();
+ ++E;
}
// After last.
while (E) {
- if (!E->get()->connected) {
- E = E->next();
+ if (!E->value->connected) {
+ ++E;
continue;
}
- for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
- next_packet_peer = E->key();
+ next_packet_peer = E->key;
return;
}
}
- E = E->next();
+ ++E;
}
- E = peer_map.front();
+ E = peer_map.begin();
// Before last
while (E) {
- if (!E->get()->connected) {
- E = E->next();
+ if (!E->value->connected) {
+ ++E;
continue;
}
- for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ for (const Ref<WebRTCDataChannel> &F : E->value->channels) {
if (F->get_available_packet_count()) {
- next_packet_peer = E->key();
+ next_packet_peer = E->key;
return;
}
}
- if (E->key() == (int)next_packet_peer) {
+ if (E->key == (int)next_packet_peer) {
break;
}
- E = E->next();
+ ++E;
}
// No packet found
next_packet_peer = 0;
@@ -354,12 +354,12 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
}
if (target_peer > 0) {
- Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(target_peer);
+ HashMap<int, Ref<ConnectedPeer>>::Iterator E = peer_map.find(target_peer);
ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + ".");
- ERR_FAIL_COND_V_MSG(E->value()->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
- ERR_FAIL_COND_V(E->value()->channels[ch].is_null(), ERR_BUG);
- return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
+ ERR_FAIL_COND_V_MSG(E->value->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value->channels.size()));
+ ERR_FAIL_COND_V(E->value->channels[ch].is_null(), ERR_BUG);
+ return E->value->channels[ch]->put_packet(p_buffer, p_buffer_size);
} else {
int exclude = -target_peer;
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index 6675c67867..97550a3e9d 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -69,7 +69,7 @@ private:
int next_packet_peer = 0;
bool server_compat = false;
- Map<int, Ref<ConnectedPeer>> peer_map;
+ HashMap<int, Ref<ConnectedPeer>> peer_map;
List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 7fdf26d3cd..75716017d7 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -32,13 +32,14 @@
#ifdef JAVASCRIPT_ENABLED
#include "webrtc_peer_connection_js.h"
-#else
-#include "webrtc_peer_connection_extension.h"
#endif
+#include "webrtc_peer_connection_extension.h"
+
StringName WebRTCPeerConnection::default_extension;
void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) {
+ ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_extension, WebRTCPeerConnectionExtension::get_class_static()), vformat("Can't make %s the default WebRTC extension since it does not extend WebRTCPeerConnectionExtension.", p_extension));
default_extension = p_extension;
}
@@ -56,6 +57,8 @@ WebRTCPeerConnection *WebRTCPeerConnection::create() {
}
void WebRTCPeerConnection::_bind_methods() {
+ ClassDB::bind_static_method(get_class_static(), D_METHOD("set_default_extension", "extension_class"), &WebRTCPeerConnectionExtension::set_default_extension);
+
ClassDB::bind_method(D_METHOD("initialize", "configuration"), &WebRTCPeerConnection::initialize, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_data_channel", "label", "options"), &WebRTCPeerConnection::create_data_channel, DEFVAL(Dictionary()));
ClassDB::bind_method(D_METHOD("create_offer"), &WebRTCPeerConnection::create_offer);
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
index 3bc7de217e..85c04b3b19 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.cpp
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -31,8 +31,6 @@
#include "webrtc_peer_connection_extension.h"
void WebRTCPeerConnectionExtension::_bind_methods() {
- ClassDB::bind_method(D_METHOD("make_default"), &WebRTCPeerConnectionExtension::make_default);
-
GDVIRTUAL_BIND(_get_connection_state);
GDVIRTUAL_BIND(_initialize, "p_config");
GDVIRTUAL_BIND(_create_data_channel, "p_label", "p_config");
@@ -44,11 +42,6 @@ void WebRTCPeerConnectionExtension::_bind_methods() {
GDVIRTUAL_BIND(_close);
}
-void WebRTCPeerConnectionExtension::make_default() {
- ERR_FAIL_COND_MSG(!_get_extension(), vformat("Can't make %s the default without extending it.", get_class()));
- WebRTCPeerConnection::set_default_extension(get_class());
-}
-
WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionExtension::get_connection_state() const {
int state;
if (GDVIRTUAL_CALL(_get_connection_state, state)) {
diff --git a/modules/webrtc/webrtc_peer_connection_extension.h b/modules/webrtc/webrtc_peer_connection_extension.h
index 82e32b5602..bde19c173b 100644
--- a/modules/webrtc/webrtc_peer_connection_extension.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -44,8 +44,6 @@ protected:
static void _bind_methods();
public:
- void make_default();
-
virtual ConnectionState get_connection_state() const override;
virtual Error initialize(Dictionary p_config = Dictionary()) override;
diff --git a/modules/webrtc/webrtc_peer_connection_js.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index 90e19fe4f1..ee3a302fa2 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -57,7 +57,7 @@ void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) {
WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
- peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
+ peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannel>(memnew(WebRTCDataChannelJS(p_id))));
}
void WebRTCPeerConnectionJS::close() {
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index 8fa5ea7779..76b8c7fff8 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -52,6 +52,8 @@ extern int godot_js_rtc_pc_datachannel_create(int p_id, const char *p_label, con
}
class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
+ GDCLASS(WebRTCPeerConnectionJS, WebRTCPeerConnection);
+
private:
int _js_id;
ConnectionState _conn_state;
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 43d9d59f38..db529a669d 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -62,7 +62,7 @@ protected:
};
List<Packet> _incoming_packets;
- Map<int, Ref<WebSocketPeer>> _peer_map;
+ HashMap<int, Ref<WebSocketPeer>> _peer_map;
Packet _current_packet;
bool _is_multiplayer = false;
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index 894ba7766f..478dbb9d47 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -113,7 +113,7 @@ bool WSLClient::_verify_headers(String &r_protocol) {
ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1", false, "Invalid protocol. Got: '" + req[0] + "', expected 'HTTP/1.1'.");
ERR_FAIL_COND_V_MSG(req[1] != "101", false, "Invalid status code. Got: '" + req[1] + "', expected '101'.");
- Map<String, String> headers;
+ HashMap<String, String> headers;
for (int i = 1; i < len; i++) {
Vector<String> header = psa[i].split(":", false, 1);
ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i] + ".");
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index b58b2e4724..517b9643f8 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -46,7 +46,7 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St
ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
r_resource_name = req[1];
- Map<String, String> headers;
+ HashMap<String, String> headers;
for (int i = 1; i < len; i++) {
Vector<String> header = psa[i].split(":", false, 1);
ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i]);