summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/basis_universal/register_types.cpp4
-rw-r--r--modules/bmp/image_loader_bmp.cpp8
-rw-r--r--modules/bullet/SCsub11
-rw-r--r--modules/bullet/area_bullet.cpp180
-rw-r--r--modules/bullet/area_bullet.h59
-rw-r--r--modules/bullet/bullet_physics_server.cpp387
-rw-r--r--modules/bullet/bullet_physics_server.h6
-rw-r--r--modules/bullet/collision_object_bullet.cpp17
-rw-r--r--modules/bullet/collision_object_bullet.h6
-rw-r--r--modules/bullet/godot_result_callbacks.cpp48
-rw-r--r--modules/bullet/godot_result_callbacks.h6
-rw-r--r--modules/bullet/rigid_body_bullet.cpp14
-rw-r--r--modules/bullet/shape_bullet.cpp24
-rw-r--r--modules/bullet/shape_bullet.h6
-rw-r--r--modules/bullet/soft_body_bullet.cpp70
-rw-r--r--modules/bullet/soft_body_bullet.h8
-rw-r--r--modules/bullet/space_bullet.cpp164
-rw-r--r--modules/bullet/space_bullet.h12
-rw-r--r--modules/camera/camera_osx.mm24
-rw-r--r--modules/csg/csg.cpp74
-rw-r--r--modules/csg/csg.h2
-rw-r--r--modules/csg/csg_shape.cpp459
-rw-r--r--modules/csg/csg_shape.h18
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGCombiner3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGCylinder3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGMesh3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml17
-rw-r--r--modules/csg/doc_classes/CSGPrimitive3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGSphere3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml4
-rw-r--r--modules/denoise/config.py11
-rw-r--r--modules/enet/doc_classes/ENetConnection.xml6
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml12
-rw-r--r--modules/enet/doc_classes/ENetPacketPeer.xml1
-rw-r--r--modules/enet/enet_connection.cpp88
-rw-r--r--modules/enet/enet_connection.h2
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp310
-rw-r--r--modules/enet/enet_multiplayer_peer.h48
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp4
-rw-r--r--modules/fbx/data/fbx_material.cpp8
-rw-r--r--modules/fbx/data/fbx_mesh_data.cpp10
-rw-r--r--modules/fbx/data/fbx_mesh_data.h6
-rw-r--r--modules/fbx/data/fbx_skeleton.cpp13
-rw-r--r--modules/fbx/doc_classes/EditorSceneImporterFBX.xml36
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp119
-rw-r--r--modules/fbx/editor_scene_importer_fbx.h8
-rw-r--r--modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp46
-rw-r--r--modules/fbx/fbx_parser/FBXCommon.h4
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.h26
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.h4
-rw-r--r--modules/fbx/fbx_parser/FBXImportSettings.h68
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.cpp2
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.h6
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp7
-rw-r--r--modules/fbx/fbx_parser/FBXParser.h2
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp12
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.cpp8
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.h34
-rw-r--r--modules/fbx/register_types.cpp4
-rw-r--r--modules/fbx/tools/import_utils.cpp24
-rw-r--r--modules/fbx/tools/import_utils.h20
-rw-r--r--modules/fbx/tools/validation_tools.h6
-rw-r--r--modules/gdnative/SCsub3
-rw-r--r--modules/gdnative/config.py5
-rw-r--r--modules/gdnative/doc_classes/GDNative.xml2
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml6
-rw-r--r--modules/gdnative/doc_classes/NativeScript.xml4
-rw-r--r--modules/gdnative/doc_classes/PluginScript.xml2
-rw-r--r--modules/gdnative/doc_classes/VideoStreamGDNative.xml2
-rw-r--r--modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml13
-rw-r--r--modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml13
-rw-r--r--modules/gdnative/gdnative/gdnative.cpp6
-rw-r--r--modules/gdnative/gdnative_api.json574
-rw-r--r--modules/gdnative/gdnative_builders.py2
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp22
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h4
-rw-r--r--modules/gdnative/include/net/godot_net.h122
-rw-r--r--modules/gdnative/include/net/godot_webrtc.h123
-rw-r--r--modules/gdnative/include/text/godot_text.h283
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp6
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp4
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp135
-rw-r--r--modules/gdnative/nativescript/nativescript.h9
-rw-r--r--modules/gdnative/net/SCsub12
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp136
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.h79
-rw-r--r--modules/gdnative/net/register_types.cpp43
-rw-r--r--modules/gdnative/net/register_types.h37
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.h60
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h4
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp22
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h4
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp6
-rw-r--r--modules/gdnative/register_types.cpp3
-rw-r--r--modules/gdnative/text/SCsub6
-rw-r--r--modules/gdnative/text/config.py6
-rw-r--r--modules/gdnative/text/register_types.cpp36
-rw-r--r--modules/gdnative/text/register_types.h37
-rw-r--r--modules/gdnative/text/text_server_gdnative.cpp1086
-rw-r--r--modules/gdnative/text/text_server_gdnative.h254
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp2
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml33
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml4
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp14
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h4
-rw-r--r--modules/gdscript/gdscript.cpp249
-rw-r--r--modules/gdscript/gdscript.h8
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp468
-rw-r--r--modules/gdscript/gdscript_analyzer.h10
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp70
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h9
-rw-r--r--modules/gdscript/gdscript_cache.cpp26
-rw-r--r--modules/gdscript/gdscript_cache.h1
-rw-r--r--modules/gdscript/gdscript_codegen.h5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp211
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp8
-rw-r--r--modules/gdscript/gdscript_editor.cpp102
-rw-r--r--modules/gdscript/gdscript_function.cpp8
-rw-r--r--modules/gdscript/gdscript_function.h8
-rw-r--r--modules/gdscript/gdscript_parser.cpp409
-rw-r--r--modules/gdscript/gdscript_parser.h20
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp18
-rw-r--r--modules/gdscript/gdscript_tokenizer.h1
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp31
-rw-r--r--modules/gdscript/gdscript_vm.cpp79
-rw-r--r--modules/gdscript/gdscript_warning.cpp9
-rw-r--r--modules/gdscript/gdscript_warning.h2
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp11
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp27
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h5
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp7
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h5
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp4
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp97
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h4
-rw-r--r--modules/gdscript/language_server/lsp.hpp118
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp25
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out (renamed from modules/gdscript/tests/scripts/parser/errors/missing_argument.out)0
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/as.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/as.out8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_functions.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_functions.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_inline.gd46
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_inline.out10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/subscript_self.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/brace_syntax.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/brace_syntax.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_argument.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_colon.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd34
-rw-r--r--modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/array.gd16
-rw-r--r--modules/gdscript/tests/scripts/parser/features/array.out11
-rw-r--r--modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd27
-rw-r--r--modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd50
-rw-r--r--modules/gdscript/tests/scripts/parser/features/bitwise_operators.out14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class.gd25
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_inheritance.gd33
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_inheritance.out10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_name.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/class_name.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/concatenation.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/concatenation.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/constants.gd11
-rw-r--r--modules/gdscript/tests/scripts/parser/features/constants.out33
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary.gd37
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary.out14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd9
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd20
-rw-r--r--modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/enum.gd14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/enum.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.gd18
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.out8
-rw-r--r--modules/gdscript/tests/scripts/parser/features/float_notation.gd24
-rw-r--r--modules/gdscript/tests/scripts/parser/features/float_notation.out19
-rw-r--r--modules/gdscript/tests/scripts/parser/features/for_range.gd39
-rw-r--r--modules/gdscript/tests/scripts/parser/features/for_range.out58
-rw-r--r--modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/function_many_parameters.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/function_many_parameters.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/in.gd14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/in.out11
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_callable.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_callable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_named_callable.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_named_callable.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match.gd18
-rw-r--r--modules/gdscript/tests/scripts/parser/features/match.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_arrays.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_if.gd14
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_if.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_strings.gd15
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_strings.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_vector.gd17
-rw-r--r--modules/gdscript/tests/scripts/parser/features/multiline_vector.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd22
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out82
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_array.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_array.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_dictionary.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_function_calls.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_if.gd57
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_if.out21
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_match.gd79
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_match.out22
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd65
-rw-r--r--modules/gdscript/tests/scripts/parser/features/nested_parentheses.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/number_separators.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/features/number_separators.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/operator_assign.gd8
-rw-r--r--modules/gdscript/tests/scripts/parser/features/operator_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd37
-rw-r--r--modules/gdscript/tests/scripts/parser/features/property_setter_getter.out19
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.gd22
-rw-r--r--modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.out10
-rw-r--r--modules/gdscript/tests/scripts/parser/features/signal_declaration.gd20
-rw-r--r--modules/gdscript/tests/scripts/parser/features/signal_declaration.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/single_line_declaration.gd11
-rw-r--r--modules/gdscript/tests/scripts/parser/features/single_line_declaration.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/space_indentation.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/features/space_indentation.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/features/static_typing.gd13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/static_typing.out21
-rw-r--r--modules/gdscript/tests/scripts/parser/features/string_formatting.gd18
-rw-r--r--modules/gdscript/tests/scripts/parser/features/string_formatting.out11
-rw-r--r--modules/gdscript/tests/scripts/parser/features/super.gd60
-rw-r--r--modules/gdscript/tests/scripts/parser/features/super.out13
-rw-r--r--modules/gdscript/tests/scripts/parser/features/truthiness.gd30
-rw-r--r--modules/gdscript/tests/scripts/parser/features/truthiness.out65
-rw-r--r--modules/gdscript/tests/scripts/parser/features/typed_arrays.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/typed_arrays.out3
-rw-r--r--modules/gdscript/tests/scripts/parser/features/variable_declaration.gd22
-rw-r--r--modules/gdscript/tests/scripts/parser/features/variable_declaration.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/features/while.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/while.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out13
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd8
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd8
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/integer_division.gd10
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/integer_division.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd8
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd8
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd9
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out21
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd12
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_argument.out5
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd4
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/warnings/void_assignment.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd32
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd13
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd138
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd138
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd138
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd138
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out35
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lua_assign.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lua_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/recursion.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/recursion.out125
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.gd42
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.out34
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp4
-rw-r--r--modules/glslang/SCsub53
-rw-r--r--modules/glslang/register_types.cpp2
-rw-r--r--modules/gltf/config.py5
-rw-r--r--modules/gltf/doc_classes/EditorSceneImporterGLTF.xml13
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFAnimation.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFBufferView.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml12
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml28
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml73
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml (renamed from modules/gdnative/doc_classes/PacketPeerGDNative.xml)6
-rw-r--r--modules/gltf/doc_classes/GLTFLight.xml22
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFSkeleton.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFSkin.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFSpecGloss.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml4
-rw-r--r--modules/gltf/doc_classes/PackedSceneGLTF.xml43
-rw-r--r--modules/gltf/editor_scene_exporter_gltf_plugin.cpp16
-rw-r--r--modules/gltf/editor_scene_exporter_gltf_plugin.h5
-rw-r--r--modules/gltf/editor_scene_importer_gltf.cpp126
-rw-r--r--modules/gltf/editor_scene_importer_gltf.h39
-rw-r--r--modules/gltf/gltf_accessor.cpp2
-rw-r--r--modules/gltf/gltf_accessor.h5
-rw-r--r--modules/gltf/gltf_animation.h6
-rw-r--r--modules/gltf/gltf_buffer_view.cpp2
-rw-r--r--modules/gltf/gltf_camera.cpp12
-rw-r--r--modules/gltf/gltf_camera.h12
-rw-r--r--modules/gltf/gltf_document.cpp1677
-rw-r--r--modules/gltf/gltf_document.h106
-rw-r--r--modules/gltf/gltf_document_extension.cpp (renamed from modules/gdnative/net/webrtc_gdnative.cpp)72
-rw-r--r--modules/gltf/gltf_document_extension.h (renamed from modules/gdnative/net/packet_peer_gdnative.h)44
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.cpp (renamed from modules/gdnative/net/packet_peer_gdnative.cpp)86
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.h (renamed from modules/webm/register_types.cpp)34
-rw-r--r--modules/gltf/gltf_light.cpp14
-rw-r--r--modules/gltf/gltf_light.h14
-rw-r--r--modules/gltf/gltf_mesh.cpp17
-rw-r--r--modules/gltf/gltf_mesh.h12
-rw-r--r--modules/gltf/gltf_node.cpp14
-rw-r--r--modules/gltf/gltf_node.h7
-rw-r--r--modules/gltf/gltf_state.h7
-rw-r--r--modules/gltf/register_types.cpp9
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml4
-rw-r--r--modules/gridmap/grid_map.cpp111
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp128
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h4
-rw-r--r--modules/hdr/image_loader_hdr.cpp6
-rw-r--r--modules/lightmapper_rd/SCsub3
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp174
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h28
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl34
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl157
-rw-r--r--modules/lightmapper_rd/lm_raster.glsl12
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp2
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.h2
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.h2
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.h2
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp40
-rw-r--r--modules/minimp3/audio_stream_mp3.h4
-rw-r--r--modules/minimp3/doc_classes/AudioStreamMP3.xml4
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp4
-rw-r--r--modules/minimp3/resource_importer_mp3.h4
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml5
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp109
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h55
-rw-r--r--modules/mobile_vr/register_types.cpp17
-rw-r--r--modules/mono/SCsub3
-rw-r--r--modules/mono/SdkPackageVersions.props5
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py4
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py1
-rw-r--r--modules/mono/config.py2
-rw-r--r--modules/mono/csharp_script.cpp168
-rw-r--r--modules/mono/csharp_script.h14
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml7
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml29
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs16
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs19
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs32
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs80
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs15
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs124
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs44
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs22
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs85
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs85
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs26
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs23
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs12
-rw-r--r--modules/mono/editor/bindings_generator.cpp47
-rw-r--r--modules/mono/editor/bindings_generator.h4
-rw-r--r--modules/mono/editor/code_completion.cpp6
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs322
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs138
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs4
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs300
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs72
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs321
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs149
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs40
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs150
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs19
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs8
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs401
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs135
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs247
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs29
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs162
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs36
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs133
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs271
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs39
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs172
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs170
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs15
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs431
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs236
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs116
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs370
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs308
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs392
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs320
-rw-r--r--modules/mono/glue/callable_glue.cpp (renamed from modules/gdnative/net/stream_peer_gdnative.cpp)70
-rw-r--r--modules/mono/glue/collections_glue.cpp4
-rw-r--r--modules/mono/glue/gd_glue.cpp23
-rw-r--r--modules/mono/glue/glue_header.h2
-rw-r--r--modules/mono/godotsharp_dirs.cpp5
-rw-r--r--modules/mono/mono_gc_handle.h3
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp62
-rw-r--r--modules/mono/mono_gd/gd_mono.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp12
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp15
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp11
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp155
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h56
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h5
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp8
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h2
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp2
-rw-r--r--modules/mono/utils/path_utils.cpp1
-rw-r--r--modules/mono/utils/string_utils.cpp50
-rw-r--r--modules/navigation/godot_navigation_server.cpp90
-rw-r--r--modules/navigation/nav_map.cpp16
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp69
-rw-r--r--modules/navigation/navigation_mesh_generator.h8
-rw-r--r--modules/ogg/SCsub3
-rw-r--r--modules/ogg/config.py11
-rw-r--r--modules/ogg/doc_classes/OGGPacketSequence.xml30
-rw-r--r--modules/ogg/doc_classes/OGGPacketSequencePlayback.xml (renamed from modules/gdnative/doc_classes/StreamPeerGDNative.xml)6
-rw-r--r--modules/ogg/ogg_packet_sequence.cpp220
-rw-r--r--modules/ogg/ogg_packet_sequence.h128
-rw-r--r--modules/ogg/register_types.cpp7
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml9
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml2
-rw-r--r--modules/opensimplex/noise_texture.cpp2
-rw-r--r--modules/opensimplex/noise_texture.h3
-rw-r--r--modules/opus/SCsub252
-rw-r--r--modules/opus/config.py6
-rw-r--r--modules/opus/register_types.cpp37
-rw-r--r--modules/opus/register_types.h37
-rw-r--r--modules/pvr/texture_loader_pvr.cpp6
-rw-r--r--modules/raycast/SCsub18
-rw-r--r--modules/raycast/config.py2
-rw-r--r--modules/raycast/godot_update_embree.py12
-rw-r--r--modules/raycast/lightmap_raycaster.cpp9
-rw-r--r--modules/raycast/lightmap_raycaster.h2
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp209
-rw-r--r--modules/raycast/raycast_occlusion_cull.h31
-rw-r--r--modules/raycast/register_types.cpp5
-rw-r--r--modules/raycast/static_raycaster.cpp137
-rw-r--r--modules/raycast/static_raycaster.h (renamed from modules/stb_vorbis/register_types.cpp)50
-rw-r--r--modules/regex/config.py2
-rw-r--r--modules/regex/doc_classes/RegEx.xml2
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml2
-rw-r--r--modules/stb_vorbis/SCsub27
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp274
-rw-r--r--modules/stb_vorbis/config.py16
-rw-r--r--modules/stb_vorbis/register_types.h37
-rw-r--r--modules/stb_vorbis/resource_importer_ogg_vorbis.cpp104
-rw-r--r--modules/text_server_adv/SCsub7
-rw-r--r--modules/text_server_adv/config.py10
-rw-r--r--modules/text_server_adv/doc_classes/TextServerAdvanced.xml10
-rw-r--r--modules/text_server_adv/register_types.cpp7
-rw-r--r--modules/text_server_adv/script_iterator.cpp20
-rw-r--r--modules/text_server_adv/script_iterator.h2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2015
-rw-r--r--modules/text_server_adv/text_server_adv.h160
-rw-r--r--modules/text_server_fb/config.py10
-rw-r--r--modules/text_server_fb/doc_classes/TextServerFallback.xml10
-rw-r--r--modules/text_server_fb/register_types.cpp7
-rw-r--r--modules/text_server_fb/text_server_fb.cpp1031
-rw-r--r--modules/text_server_fb/text_server_fb.h120
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml4
-rw-r--r--modules/theora/video_stream_theora.cpp6
-rw-r--r--modules/tinyexr/SCsub3
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp2
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp2
-rw-r--r--modules/upnp/SCsub4
-rw-r--r--modules/upnp/doc_classes/UPNP.xml32
-rw-r--r--modules/upnp/upnp.cpp14
-rw-r--r--modules/upnp/upnp.h2
-rw-r--r--modules/upnp/upnp_device.cpp2
-rw-r--r--modules/vhacd/register_types.cpp69
-rw-r--r--modules/visual_script/SCsub3
-rw-r--r--modules/visual_script/config.py1
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml54
-rw-r--r--modules/visual_script/doc_classes/VisualScriptClassConstant.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComment.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComposeArray.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCondition.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstant.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptConstructor.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNodes.xml37
-rw-r--r--modules/visual_script/doc_classes/VisualScriptDeconstruct.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEmitSignal.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptExpression.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunction.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionCall.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionState.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexGet.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexSet.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptInputAction.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIterator.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLists.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVar.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptNode.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptOperator.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPreload.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertyGet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertySet.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptResourcePath.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptReturn.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneNode.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSceneTree.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelect.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSelf.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSequence.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSubCall.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSwitch.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptTypeCast.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableGet.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableSet.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptWhile.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYield.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYieldSignal.xml2
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp (renamed from modules/visual_script/visual_script_editor.cpp)486
-rw-r--r--modules/visual_script/editor/visual_script_editor.h (renamed from modules/visual_script/visual_script_editor.h)12
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp (renamed from modules/visual_script/visual_script_property_selector.cpp)34
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h (renamed from modules/visual_script/visual_script_property_selector.h)0
-rw-r--r--modules/visual_script/register_types.cpp5
-rw-r--r--modules/visual_script/visual_script.cpp76
-rw-r--r--modules/visual_script/visual_script.h6
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp93
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h7
-rw-r--r--modules/visual_script/visual_script_expression.cpp8
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp8
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp40
-rw-r--r--modules/visual_script/visual_script_nodes.cpp38
-rw-r--r--modules/visual_script/visual_script_nodes.h12
-rw-r--r--modules/visual_script/visual_script_yield_nodes.cpp8
-rw-r--r--modules/vorbis/SCsub3
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp435
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.h (renamed from modules/stb_vorbis/audio_stream_ogg_vorbis.h)56
-rw-r--r--modules/vorbis/config.py11
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml (renamed from modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml)12
-rw-r--r--modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml (renamed from modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml)6
-rw-r--r--modules/vorbis/register_types.cpp15
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp190
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h (renamed from modules/stb_vorbis/resource_importer_ogg_vorbis.h)24
-rw-r--r--modules/webm/SCsub48
-rw-r--r--modules/webm/config.py19
-rw-r--r--modules/webm/doc_classes/VideoStreamWebm.xml30
-rw-r--r--modules/webm/libvpx/SCsub382
-rw-r--r--modules/webm/register_types.h37
-rw-r--r--modules/webm/video_stream_webm.cpp469
-rw-r--r--modules/webm/video_stream_webm.h135
-rw-r--r--modules/webp/SCsub1
-rw-r--r--modules/webrtc/SCsub5
-rw-r--r--modules/webrtc/config.py2
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml106
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml11
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml3
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml71
-rw-r--r--modules/webrtc/register_types.cpp26
-rw-r--r--modules/webrtc/webrtc_data_channel_extension.cpp215
-rw-r--r--modules/webrtc/webrtc_data_channel_extension.h (renamed from modules/webrtc/webrtc_data_channel_gdnative.h)53
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.cpp143
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp93
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h12
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp24
-rw-r--r--modules/webrtc/webrtc_peer_connection.h7
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.cpp131
-rw-r--r--modules/webrtc/webrtc_peer_connection_extension.h (renamed from modules/webrtc/webrtc_peer_connection_gdnative.h)47
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.cpp121
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h3
-rw-r--r--modules/websocket/SCsub3
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml7
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml9
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml2
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml5
-rw-r--r--modules/websocket/editor_debugger_server_websocket.cpp44
-rw-r--r--modules/websocket/editor_debugger_server_websocket.h14
-rw-r--r--modules/websocket/emws_peer.cpp7
-rw-r--r--modules/websocket/websocket_client.cpp2
-rw-r--r--modules/websocket/websocket_macros.h6
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp45
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h9
-rw-r--r--modules/websocket/websocket_server.cpp8
-rw-r--r--modules/websocket/wsl_peer.cpp8
-rw-r--r--modules/websocket/wsl_server.cpp18
-rw-r--r--modules/websocket/wsl_server.h2
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml4
-rw-r--r--modules/webxr/register_types.cpp21
-rw-r--r--modules/webxr/webxr_interface_js.cpp88
-rw-r--r--modules/webxr/webxr_interface_js.h5
-rw-r--r--modules/xatlas_unwrap/register_types.cpp2
824 files changed, 18583 insertions, 13352 deletions
diff --git a/modules/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index 9a13406900..d2105d7c5d 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -203,7 +203,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
format = basist::transcoder_texture_format::cTFETC2; // get this from renderer
imgfmt = Image::FORMAT_ETC2_RGBA8;
} else {
- //gles2 most likely
+ //opengl most likely
format = basist::transcoder_texture_format::cTFRGBA4444; // get this from renderer
imgfmt = Image::FORMAT_RGBA4444;
}
@@ -216,7 +216,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
format = basist::transcoder_texture_format::cTFETC2; // get this from renderer
imgfmt = Image::FORMAT_ETC2_RGBA8;
} else {
- //gles2 most likely, bad for normal maps, nothing to do about this.
+ //opengl most likely, bad for normal maps, nothing to do about this.
format = basist::transcoder_texture_format::cTFRGBA32;
imgfmt = Image::FORMAT_RGBA8;
}
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index 171895ed24..3d47055247 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -91,11 +91,13 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
// the data width in case of 8/4/1 bit images
const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes;
const uint8_t *line = p_buffer + (line_width * (height - 1));
+ const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset;
for (uint64_t i = 0; i < height; i++) {
const uint8_t *line_ptr = line;
for (unsigned int j = 0; j < w; j++) {
+ ERR_FAIL_COND_V(line_ptr >= end_buffer, ERR_FILE_CORRUPT);
switch (bits_per_pixel) {
case 1: {
uint8_t color_index = *line_ptr;
@@ -246,8 +248,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
}
// Don't rely on sizeof(bmp_file_header) as structure padding
// adds 2 bytes offset leading to misaligned color table reading
- uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE +
- bmp_header.bmp_info_header.bmp_header_size;
+ uint32_t ct_offset = BITMAP_FILE_HEADER_SIZE + bmp_header.bmp_info_header.bmp_header_size;
f->seek(ct_offset);
uint32_t color_table_size = 0;
@@ -269,8 +270,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
f->seek(bmp_header.bmp_file_header.bmp_file_offset);
- uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size -
- bmp_header.bmp_file_header.bmp_file_offset);
+ uint32_t bmp_buffer_size = (bmp_header.bmp_file_header.bmp_file_size - bmp_header.bmp_file_header.bmp_file_offset);
Vector<uint8_t> bmp_buffer;
err = bmp_buffer.resize(bmp_buffer_size);
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index ba57de303e..09509abc44 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -10,7 +10,7 @@ env_bullet = env_modules.Clone()
thirdparty_obj = []
if env["builtin_bullet"]:
- # Build only version 2 for now (as of 2.89)
+ # Build only "Bullet2" API (not "Bullet3" folders).
# Sync file list with relevant upstream CMakeLists.txt for each folder.
if env["float"] == "64":
env.Append(CPPDEFINES=["BT_USE_DOUBLE_PRECISION=1"])
@@ -189,6 +189,7 @@ if env["builtin_bullet"]:
"LinearMath/btGeometryUtil.cpp",
"LinearMath/btPolarDecomposition.cpp",
"LinearMath/btQuickprof.cpp",
+ "LinearMath/btReducedVector.cpp",
"LinearMath/btSerializer.cpp",
"LinearMath/btSerializer64.cpp",
"LinearMath/btThreads.cpp",
@@ -200,15 +201,11 @@ if env["builtin_bullet"]:
thirdparty_sources = [thirdparty_dir + file for file in bullet2_src]
- # Treat Bullet headers as system headers to avoid raising warnings. Not supported on MSVC.
- if not env.msvc:
- env_bullet.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
- else:
- env_bullet.Prepend(CPPPATH=[thirdparty_dir])
+ env_bullet.Prepend(CPPPATH=[thirdparty_dir])
if env["target"] == "debug" or env["target"] == "release_debug":
env_bullet.Append(CPPDEFINES=["DEBUG"])
- env_bullet.Append(CPPDEFINES=["BT_USE_OLD_DAMPING_METHOD"])
+ env_bullet.Append(CPPDEFINES=["BT_USE_OLD_DAMPING_METHOD", "BT_THREADSAFE"])
env_thirdparty = env_bullet.Clone()
env_thirdparty.disable_warnings()
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index c3bd84c329..10a71d65df 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -59,8 +59,8 @@ AreaBullet::AreaBullet() :
AreaBullet::~AreaBullet() {
// signal are handled by godot, so just clear without notify
- for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- overlappingObjects[i].object->on_exit_area(this);
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ overlapping_shapes[i].other_object->on_exit_area(this);
}
}
@@ -70,89 +70,140 @@ void AreaBullet::dispatch_callbacks() {
}
isScratched = false;
- // Reverse order because I've to remove EXIT objects
- for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- OverlappingObjectData &otherObj = overlappingObjects.write[i];
+ // Reverse order so items can be removed.
+ for (int i = overlapping_shapes.size() - 1; i >= 0; i--) {
+ OverlappingShapeData &overlapping_shape = overlapping_shapes.write[i];
- switch (otherObj.state) {
+ switch (overlapping_shape.state) {
case OVERLAP_STATE_ENTER:
- otherObj.state = OVERLAP_STATE_INSIDE;
- call_event(otherObj.object, PhysicsServer3D::AREA_BODY_ADDED);
- otherObj.object->on_enter_area(this);
+ overlapping_shape.state = OVERLAP_STATE_INSIDE;
+ call_event(overlapping_shape, PhysicsServer3D::AREA_BODY_ADDED);
+ if (_overlapping_shape_count(overlapping_shape.other_object) == 1) {
+ // This object's first shape being added.
+ overlapping_shape.other_object->on_enter_area(this);
+ }
break;
case OVERLAP_STATE_EXIT:
- call_event(otherObj.object, PhysicsServer3D::AREA_BODY_REMOVED);
- otherObj.object->on_exit_area(this);
- overlappingObjects.remove(i); // Remove after callback
+ call_event(overlapping_shape, PhysicsServer3D::AREA_BODY_REMOVED);
+ if (_overlapping_shape_count(overlapping_shape.other_object) == 1) {
+ // This object's last shape being removed.
+ overlapping_shape.other_object->on_exit_area(this);
+ }
+ overlapping_shapes.remove_at(i); // Remove after callback
break;
+ case OVERLAP_STATE_INSIDE: {
+ if (overlapping_shape.other_object->getType() == TYPE_RIGID_BODY) {
+ RigidBodyBullet *body = static_cast<RigidBodyBullet *>(overlapping_shape.other_object);
+ body->scratch_space_override_modificator();
+ }
+ break;
+ }
case OVERLAP_STATE_DIRTY:
- case OVERLAP_STATE_INSIDE:
break;
}
}
}
-void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status) {
- InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_otherObject->getType())];
- Object *areaGodoObject = ObjectDB::get_instance(event.event_callback_id);
+void AreaBullet::call_event(const OverlappingShapeData &p_overlapping_shape, PhysicsServer3D::AreaBodyStatus p_status) {
+ InOutEventCallback &event = eventsCallbacks[static_cast<int>(p_overlapping_shape.other_object->getType())];
- if (!areaGodoObject) {
- event.event_callback_id = ObjectID();
+ if (!event.event_callback.is_valid()) {
+ event.event_callback = Callable();
return;
}
call_event_res[0] = p_status;
- call_event_res[1] = p_otherObject->get_self(); // Other body
- call_event_res[2] = p_otherObject->get_instance_id(); // instance ID
- call_event_res[3] = 0; // other_body_shape ID
- call_event_res[4] = 0; // self_shape ID
+ call_event_res[1] = p_overlapping_shape.other_object->get_self(); // RID
+ call_event_res[2] = p_overlapping_shape.other_object->get_instance_id(); // Object ID
+ call_event_res[3] = p_overlapping_shape.other_shape_id; // Other object's shape ID
+ call_event_res[4] = p_overlapping_shape.our_shape_id; // This area's shape ID
Callable::CallError outResp;
- areaGodoObject->call(event.event_callback_method, (const Variant **)call_event_res_ptr, 5, outResp);
+ Variant ret;
+ event.event_callback.call((const Variant **)call_event_res, 5, ret, outResp);
}
-void AreaBullet::scratch() {
- if (isScratched) {
- return;
+int AreaBullet::_overlapping_shape_count(CollisionObjectBullet *p_other_object) {
+ int count = 0;
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ if (overlapping_shapes[i].other_object == p_other_object) {
+ count++;
+ }
}
- isScratched = true;
+ return count;
}
-void AreaBullet::clear_overlaps(bool p_notify) {
- for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- if (p_notify) {
- call_event(overlappingObjects[i].object, PhysicsServer3D::AREA_BODY_REMOVED);
+int AreaBullet::_find_overlapping_shape(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id) {
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ const OverlappingShapeData &overlapping_shape = overlapping_shapes[i];
+ if (overlapping_shape.other_object == p_other_object && overlapping_shape.other_shape_id == p_other_shape_id && overlapping_shape.our_shape_id == p_our_shape_id) {
+ return i;
}
- overlappingObjects[i].object->on_exit_area(this);
}
- overlappingObjects.clear();
+ return -1;
}
-void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
- for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- if (overlappingObjects[i].object == p_object) {
- if (p_notify) {
- call_event(overlappingObjects[i].object, PhysicsServer3D::AREA_BODY_REMOVED);
- }
- overlappingObjects[i].object->on_exit_area(this);
- overlappingObjects.remove(i);
- break;
+void AreaBullet::mark_all_overlaps_dirty() {
+ OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ // Don't overwrite OVERLAP_STATE_ENTER state.
+ if (overlapping_shapes_w[i].state != OVERLAP_STATE_ENTER) {
+ overlapping_shapes_w[i].state = OVERLAP_STATE_DIRTY;
}
}
}
-int AreaBullet::find_overlapping_object(CollisionObjectBullet *p_colObj) {
- const int size = overlappingObjects.size();
- for (int i = 0; i < size; ++i) {
- if (overlappingObjects[i].object == p_colObj) {
- return i;
+void AreaBullet::mark_object_overlaps_inside(CollisionObjectBullet *p_other_object) {
+ OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ if (overlapping_shapes_w[i].other_object == p_other_object && overlapping_shapes_w[i].state == OVERLAP_STATE_DIRTY) {
+ overlapping_shapes_w[i].state = OVERLAP_STATE_INSIDE;
}
}
- return -1;
+}
+
+void AreaBullet::set_overlap(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id) {
+ int i = _find_overlapping_shape(p_other_object, p_other_shape_id, p_our_shape_id);
+ if (i == -1) { // Not found, create new one.
+ OverlappingShapeData overlapping_shape(p_other_object, OVERLAP_STATE_ENTER, p_other_shape_id, p_our_shape_id);
+ overlapping_shapes.push_back(overlapping_shape);
+ p_other_object->notify_new_overlap(this);
+ isScratched = true;
+ } else {
+ overlapping_shapes.ptrw()[i].state = OVERLAP_STATE_INSIDE;
+ }
+}
+
+void AreaBullet::mark_all_dirty_overlaps_as_exit() {
+ OverlappingShapeData *overlapping_shapes_w = overlapping_shapes.ptrw();
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ if (overlapping_shapes[i].state == OVERLAP_STATE_DIRTY) {
+ overlapping_shapes_w[i].state = OVERLAP_STATE_EXIT;
+ isScratched = true;
+ }
+ }
+}
+
+void AreaBullet::remove_object_overlaps(CollisionObjectBullet *p_object) {
+ // Reverse order so items can be removed.
+ for (int i = overlapping_shapes.size() - 1; i >= 0; i--) {
+ if (overlapping_shapes[i].other_object == p_object) {
+ overlapping_shapes.remove_at(i);
+ }
+ }
+}
+
+void AreaBullet::clear_overlaps() {
+ for (int i = 0; i < overlapping_shapes.size(); i++) {
+ call_event(overlapping_shapes[i], PhysicsServer3D::AREA_BODY_REMOVED);
+ overlapping_shapes[i].other_object->on_exit_area(this);
+ }
+ overlapping_shapes.clear();
}
void AreaBullet::set_monitorable(bool p_monitorable) {
monitorable = p_monitorable;
+ updated = true;
}
bool AreaBullet::is_monitoring() const {
@@ -162,6 +213,7 @@ bool AreaBullet::is_monitoring() const {
void AreaBullet::main_shape_changed() {
CRASH_COND(!get_main_shape());
btGhost->setCollisionShape(get_main_shape());
+ updated = true;
}
void AreaBullet::reload_body() {
@@ -174,7 +226,7 @@ void AreaBullet::reload_body() {
void AreaBullet::set_space(SpaceBullet *p_space) {
// Clear the old space if there is one
if (space) {
- clear_overlaps(false);
+ clear_overlaps();
isScratched = false;
// Remove this object form the physics world
@@ -192,24 +244,7 @@ void AreaBullet::on_collision_filters_change() {
if (space) {
space->reload_collision_filters(this);
}
-}
-
-void AreaBullet::add_overlap(CollisionObjectBullet *p_otherObject) {
- scratch();
- overlappingObjects.push_back(OverlappingObjectData(p_otherObject, OVERLAP_STATE_ENTER));
- p_otherObject->notify_new_overlap(this);
-}
-
-void AreaBullet::put_overlap_as_exit(int p_index) {
- scratch();
- overlappingObjects.write[p_index].state = OVERLAP_STATE_EXIT;
-}
-
-void AreaBullet::put_overlap_as_inside(int p_index) {
- // This check is required to be sure this body was inside
- if (OVERLAP_STATE_DIRTY == overlappingObjects[p_index].state) {
- overlappingObjects.write[p_index].state = OVERLAP_STATE_INSIDE;
- }
+ updated = true;
}
void AreaBullet::set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value) {
@@ -241,6 +276,7 @@ void AreaBullet::set_param(PhysicsServer3D::AreaParameter p_param, const Variant
default:
WARN_PRINT("Area doesn't support this parameter in the Bullet backend: " + itos(p_param));
}
+ isScratched = true;
}
Variant AreaBullet::get_param(PhysicsServer3D::AreaParameter p_param) const {
@@ -267,21 +303,21 @@ Variant AreaBullet::get_param(PhysicsServer3D::AreaParameter p_param) const {
}
}
-void AreaBullet::set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method) {
+void AreaBullet::set_event_callback(Type p_callbackObjectType, const Callable &p_callback) {
InOutEventCallback &ev = eventsCallbacks[static_cast<int>(p_callbackObjectType)];
- ev.event_callback_id = p_id;
- ev.event_callback_method = p_method;
+ ev.event_callback = p_callback;
/// Set if monitoring
- if (eventsCallbacks[0].event_callback_id.is_valid() || eventsCallbacks[1].event_callback_id.is_valid()) {
+ if (!eventsCallbacks[0].event_callback.is_null() || !eventsCallbacks[1].event_callback.is_null()) {
set_godot_object_flags(get_godot_object_flags() | GOF_IS_MONITORING_AREA);
} else {
set_godot_object_flags(get_godot_object_flags() & (~GOF_IS_MONITORING_AREA));
+ clear_overlaps();
}
}
bool AreaBullet::has_event_callback(Type p_callbackObjectType) {
- return eventsCallbacks[static_cast<int>(p_callbackObjectType)].event_callback_id.is_valid();
+ return !eventsCallbacks[static_cast<int>(p_callbackObjectType)].event_callback.is_null();
}
void AreaBullet::on_enter_area(AreaBullet *p_area) {
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 7cf666c119..3bff364cc1 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -43,12 +43,9 @@
class btGhostObject;
class AreaBullet : public RigidCollisionObjectBullet {
- friend void SpaceBullet::check_ghost_overlaps();
-
public:
struct InOutEventCallback {
- ObjectID event_callback_id;
- StringName event_callback_method;
+ Callable event_callback;
InOutEventCallback() {}
};
@@ -60,21 +57,19 @@ public:
OVERLAP_STATE_EXIT // Mark ended overlaps
};
- struct OverlappingObjectData {
- CollisionObjectBullet *object = nullptr;
- OverlapState state = OVERLAP_STATE_ENTER;
-
- OverlappingObjectData() {}
- OverlappingObjectData(CollisionObjectBullet *p_object, OverlapState p_state) :
- object(p_object),
- state(p_state) {}
- OverlappingObjectData(const OverlappingObjectData &other) {
- operator=(other);
- }
- void operator=(const OverlappingObjectData &other) {
- object = other.object;
- state = other.state;
- }
+ struct OverlappingShapeData {
+ CollisionObjectBullet *other_object = nullptr;
+ OverlapState state = OVERLAP_STATE_DIRTY;
+ uint32_t other_shape_id = 0;
+ uint32_t our_shape_id = 0;
+
+ OverlappingShapeData() {}
+
+ OverlappingShapeData(CollisionObjectBullet *p_other_object, OverlapState p_state, uint32_t p_other_shape_id, uint32_t p_our_shape_id) :
+ other_object(p_other_object),
+ state(p_state),
+ other_shape_id(p_other_shape_id),
+ our_shape_id(p_our_shape_id) {}
};
private:
@@ -83,7 +78,9 @@ private:
Variant *call_event_res_ptr[5] = {};
btGhostObject *btGhost = nullptr;
- Vector<OverlappingObjectData> overlappingObjects;
+ Vector<OverlappingShapeData> overlapping_shapes;
+ int _overlapping_shape_count(CollisionObjectBullet *p_other_object);
+ int _find_overlapping_shape(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id);
bool monitorable = true;
PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
@@ -105,7 +102,6 @@ public:
~AreaBullet();
_FORCE_INLINE_ btGhostObject *get_bt_ghost() const { return btGhost; }
- int find_overlapping_object(CollisionObjectBullet *p_colObj);
void set_monitorable(bool p_monitorable);
_FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
@@ -144,26 +140,23 @@ public:
virtual void set_space(SpaceBullet *p_space);
virtual void dispatch_callbacks();
- void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
- void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
- void scratch();
-
- void clear_overlaps(bool p_notify);
- // Dispatch the callbacks and removes from overlapping list
- void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
+ void call_event(const OverlappingShapeData &p_overlapping_shape, PhysicsServer3D::AreaBodyStatus p_status);
virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
- virtual void on_collision_checker_end() { isTransformChanged = false; }
+ virtual void on_collision_checker_end() { updated = false; }
- void add_overlap(CollisionObjectBullet *p_otherObject);
- void put_overlap_as_exit(int p_index);
- void put_overlap_as_inside(int p_index);
+ void mark_all_overlaps_dirty();
+ void mark_object_overlaps_inside(CollisionObjectBullet *p_other_object);
+ void set_overlap(CollisionObjectBullet *p_other_object, uint32_t p_other_shape_id, uint32_t p_our_shape_id);
+ void mark_all_dirty_overlaps_as_exit();
+ void remove_object_overlaps(CollisionObjectBullet *p_object);
+ void clear_overlaps();
void set_param(PhysicsServer3D::AreaParameter p_param, const Variant &p_value);
Variant get_param(PhysicsServer3D::AreaParameter p_param) const;
- void set_event_callback(Type p_callbackObjectType, ObjectID p_id, const StringName &p_method);
+ void set_event_callback(Type p_callbackObjectType, const Callable &p_callback);
bool has_event_callback(Type p_callbackObjectType);
virtual void on_enter_area(AreaBullet *p_area);
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index fc876a81cf..684a20cf4d 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -87,8 +87,8 @@ RID BulletPhysicsServer3D::shape_create(ShapeType p_shape) {
ShapeBullet *shape = nullptr;
switch (p_shape) {
- case SHAPE_PLANE: {
- shape = bulletnew(PlaneShapeBullet);
+ case SHAPE_WORLD_BOUNDARY: {
+ shape = bulletnew(WorldBoundaryShapeBullet);
} break;
case SHAPE_SPHERE: {
shape = bulletnew(SphereShapeBullet);
@@ -124,7 +124,7 @@ RID BulletPhysicsServer3D::shape_create(ShapeType p_shape) {
}
void BulletPhysicsServer3D::shape_set_data(RID p_shape, const Variant &p_data) {
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
shape->set_data(p_data);
}
@@ -134,25 +134,25 @@ void BulletPhysicsServer3D::shape_set_custom_solver_bias(RID p_shape, real_t p_b
}
PhysicsServer3D::ShapeType BulletPhysicsServer3D::shape_get_type(RID p_shape) const {
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, PhysicsServer3D::SHAPE_CUSTOM);
return shape->get_type();
}
Variant BulletPhysicsServer3D::shape_get_data(RID p_shape) const {
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, Variant());
return shape->get_data();
}
void BulletPhysicsServer3D::shape_set_margin(RID p_shape, real_t p_margin) {
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
shape->set_margin(p_margin);
}
real_t BulletPhysicsServer3D::shape_get_margin(RID p_shape) const {
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, 0.0);
return shape->get_margin();
}
@@ -168,7 +168,7 @@ RID BulletPhysicsServer3D::space_create() {
}
void BulletPhysicsServer3D::space_set_active(RID p_space, bool p_active) {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
if (space_is_active(p_space) == p_active) {
@@ -185,47 +185,47 @@ void BulletPhysicsServer3D::space_set_active(RID p_space, bool p_active) {
}
bool BulletPhysicsServer3D::space_is_active(RID p_space) const {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND_V(!space, false);
return -1 != active_spaces.find(space);
}
void BulletPhysicsServer3D::space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
space->set_param(p_param, p_value);
}
real_t BulletPhysicsServer3D::space_get_param(RID p_space, SpaceParameter p_param) const {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND_V(!space, 0);
return space->get_param(p_param);
}
PhysicsDirectSpaceState3D *BulletPhysicsServer3D::space_get_direct_state(RID p_space) {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND_V(!space, nullptr);
return space->get_direct_state();
}
void BulletPhysicsServer3D::space_set_debug_contacts(RID p_space, int p_max_contacts) {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
space->set_debug_contacts(p_max_contacts);
}
Vector<Vector3> BulletPhysicsServer3D::space_get_contacts(RID p_space) const {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND_V(!space, Vector<Vector3>());
return space->get_debug_contacts();
}
int BulletPhysicsServer3D::space_get_contact_count(RID p_space) const {
- SpaceBullet *space = space_owner.getornull(p_space);
+ SpaceBullet *space = space_owner.get_or_null(p_space);
ERR_FAIL_COND_V(!space, 0);
return space->get_debug_contact_count();
@@ -239,91 +239,91 @@ RID BulletPhysicsServer3D::area_create() {
}
void BulletPhysicsServer3D::area_set_space(RID p_area, RID p_space) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
SpaceBullet *space = nullptr;
if (p_space.is_valid()) {
- space = space_owner.getornull(p_space);
+ space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
}
area->set_space(space);
}
RID BulletPhysicsServer3D::area_get_space(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
return area->get_space()->get_self();
}
void BulletPhysicsServer3D::area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_spOv_mode(p_mode);
}
PhysicsServer3D::AreaSpaceOverrideMode BulletPhysicsServer3D::area_get_space_override_mode(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED);
return area->get_spOv_mode();
}
void BulletPhysicsServer3D::area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
area->add_shape(shape, p_transform, p_disabled);
}
void BulletPhysicsServer3D::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
area->set_shape(p_shape_idx, shape);
}
void BulletPhysicsServer3D::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_shape_transform(p_shape_idx, p_transform);
}
int BulletPhysicsServer3D::area_get_shape_count(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, 0);
return area->get_shape_count();
}
RID BulletPhysicsServer3D::area_get_shape(RID p_area, int p_shape_idx) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, RID());
return area->get_shape(p_shape_idx)->get_self();
}
Transform3D BulletPhysicsServer3D::area_get_shape_transform(RID p_area, int p_shape_idx) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, Transform3D());
return area->get_shape_transform(p_shape_idx);
}
void BulletPhysicsServer3D::area_remove_shape(RID p_area, int p_shape_idx) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
return area->remove_shape_full(p_shape_idx);
}
void BulletPhysicsServer3D::area_clear_shapes(RID p_area) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
for (int i = area->get_shape_count(); 0 < i; --i) {
@@ -332,7 +332,7 @@ void BulletPhysicsServer3D::area_clear_shapes(RID p_area) {
}
void BulletPhysicsServer3D::area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_shape_disabled(p_shape_idx, p_disabled);
@@ -342,7 +342,7 @@ void BulletPhysicsServer3D::area_attach_object_instance_id(RID p_area, ObjectID
if (space_owner.owns(p_area)) {
return;
}
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_instance_id(p_id);
}
@@ -351,19 +351,19 @@ ObjectID BulletPhysicsServer3D::area_get_object_instance_id(RID p_area) const {
if (space_owner.owns(p_area)) {
return ObjectID();
}
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, ObjectID());
return area->get_instance_id();
}
void BulletPhysicsServer3D::area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) {
if (space_owner.owns(p_area)) {
- SpaceBullet *space = space_owner.getornull(p_area);
+ SpaceBullet *space = space_owner.get_or_null(p_area);
if (space) {
space->set_param(p_param, p_value);
}
} else {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_param(p_param, p_value);
@@ -372,10 +372,10 @@ void BulletPhysicsServer3D::area_set_param(RID p_area, AreaParameter p_param, co
Variant BulletPhysicsServer3D::area_get_param(RID p_area, AreaParameter p_param) const {
if (space_owner.owns(p_area)) {
- SpaceBullet *space = space_owner.getornull(p_area);
+ SpaceBullet *space = space_owner.get_or_null(p_area);
return space->get_param(p_param);
} else {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, Variant());
return area->get_param(p_param);
@@ -383,52 +383,52 @@ Variant BulletPhysicsServer3D::area_get_param(RID p_area, AreaParameter p_param)
}
void BulletPhysicsServer3D::area_set_transform(RID p_area, const Transform3D &p_transform) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_transform(p_transform);
}
Transform3D BulletPhysicsServer3D::area_get_transform(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND_V(!area, Transform3D());
return area->get_transform();
}
void BulletPhysicsServer3D::area_set_collision_mask(RID p_area, uint32_t p_mask) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_collision_mask(p_mask);
}
void BulletPhysicsServer3D::area_set_collision_layer(RID p_area, uint32_t p_layer) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_collision_layer(p_layer);
}
void BulletPhysicsServer3D::area_set_monitorable(RID p_area, bool p_monitorable) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_monitorable(p_monitorable);
}
-void BulletPhysicsServer3D::area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) {
- AreaBullet *area = area_owner.getornull(p_area);
+void BulletPhysicsServer3D::area_set_monitor_callback(RID p_area, const Callable &p_callback) {
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
- area->set_event_callback(CollisionObjectBullet::TYPE_RIGID_BODY, p_receiver ? p_receiver->get_instance_id() : ObjectID(), p_method);
+ area->set_event_callback(CollisionObjectBullet::TYPE_RIGID_BODY, p_callback.is_valid() ? p_callback : Callable());
}
-void BulletPhysicsServer3D::area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) {
- AreaBullet *area = area_owner.getornull(p_area);
+void BulletPhysicsServer3D::area_set_area_monitor_callback(RID p_area, const Callable &p_callback) {
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
- area->set_event_callback(CollisionObjectBullet::TYPE_AREA, p_receiver ? p_receiver->get_instance_id() : ObjectID(), p_method);
+ area->set_event_callback(CollisionObjectBullet::TYPE_AREA, p_callback.is_valid() ? p_callback : Callable());
}
void BulletPhysicsServer3D::area_set_ray_pickable(RID p_area, bool p_enable) {
- AreaBullet *area = area_owner.getornull(p_area);
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_ray_pickable(p_enable);
}
@@ -445,12 +445,12 @@ RID BulletPhysicsServer3D::body_create(BodyMode p_mode, bool p_init_sleeping) {
}
void BulletPhysicsServer3D::body_set_space(RID p_body, RID p_space) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
SpaceBullet *space = nullptr;
if (p_space.is_valid()) {
- space = space_owner.getornull(p_space);
+ space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
}
@@ -462,7 +462,7 @@ void BulletPhysicsServer3D::body_set_space(RID p_body, RID p_space) {
}
RID BulletPhysicsServer3D::body_get_space(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, RID());
SpaceBullet *space = body->get_space();
@@ -473,52 +473,52 @@ RID BulletPhysicsServer3D::body_get_space(RID p_body) const {
}
void BulletPhysicsServer3D::body_set_mode(RID p_body, PhysicsServer3D::BodyMode p_mode) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_mode(p_mode);
}
PhysicsServer3D::BodyMode BulletPhysicsServer3D::body_get_mode(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, BODY_MODE_STATIC);
return body->get_mode();
}
void BulletPhysicsServer3D::body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
body->add_shape(shape, p_transform, p_disabled);
}
void BulletPhysicsServer3D::body_set_shape(RID p_body, int p_shape_idx, RID p_shape) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- ShapeBullet *shape = shape_owner.getornull(p_shape);
+ ShapeBullet *shape = shape_owner.get_or_null(p_shape);
ERR_FAIL_COND(!shape);
body->set_shape(p_shape_idx, shape);
}
void BulletPhysicsServer3D::body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_shape_transform(p_shape_idx, p_transform);
}
int BulletPhysicsServer3D::body_get_shape_count(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_shape_count();
}
RID BulletPhysicsServer3D::body_get_shape(RID p_body, int p_shape_idx) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, RID());
ShapeBullet *shape = body->get_shape(p_shape_idx);
@@ -528,27 +528,27 @@ RID BulletPhysicsServer3D::body_get_shape(RID p_body, int p_shape_idx) const {
}
Transform3D BulletPhysicsServer3D::body_get_shape_transform(RID p_body, int p_shape_idx) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, Transform3D());
return body->get_shape_transform(p_shape_idx);
}
void BulletPhysicsServer3D::body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_shape_disabled(p_shape_idx, p_disabled);
}
void BulletPhysicsServer3D::body_remove_shape(RID p_body, int p_shape_idx) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->remove_shape_full(p_shape_idx);
}
void BulletPhysicsServer3D::body_clear_shapes(RID p_body) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->remove_all_shapes();
@@ -569,42 +569,42 @@ ObjectID BulletPhysicsServer3D::body_get_object_instance_id(RID p_body) const {
}
void BulletPhysicsServer3D::body_set_enable_continuous_collision_detection(RID p_body, bool p_enable) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_continuous_collision_detection(p_enable);
}
bool BulletPhysicsServer3D::body_is_continuous_collision_detection_enabled(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, false);
return body->is_continuous_collision_detection_enabled();
}
void BulletPhysicsServer3D::body_set_collision_layer(RID p_body, uint32_t p_layer) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_collision_layer(p_layer);
}
uint32_t BulletPhysicsServer3D::body_get_collision_layer(RID p_body) const {
- const RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ const RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_collision_layer();
}
void BulletPhysicsServer3D::body_set_collision_mask(RID p_body, uint32_t p_mask) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_collision_mask(p_mask);
}
uint32_t BulletPhysicsServer3D::body_get_collision_mask(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_collision_mask();
@@ -620,21 +620,21 @@ uint32_t BulletPhysicsServer3D::body_get_user_flags(RID p_body) const {
}
void BulletPhysicsServer3D::body_set_param(RID p_body, BodyParameter p_param, real_t p_value) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_param(p_param, p_value);
}
real_t BulletPhysicsServer3D::body_get_param(RID p_body, BodyParameter p_param) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_param(p_param);
}
void BulletPhysicsServer3D::body_set_kinematic_safe_margin(RID p_body, real_t p_margin) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
if (body->get_kinematic_utilities()) {
@@ -643,7 +643,7 @@ void BulletPhysicsServer3D::body_set_kinematic_safe_margin(RID p_body, real_t p_
}
real_t BulletPhysicsServer3D::body_get_kinematic_safe_margin(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
if (body->get_kinematic_utilities()) {
@@ -654,90 +654,90 @@ real_t BulletPhysicsServer3D::body_get_kinematic_safe_margin(RID p_body) const {
}
void BulletPhysicsServer3D::body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_state(p_state, p_variant);
}
Variant BulletPhysicsServer3D::body_get_state(RID p_body, BodyState p_state) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, Variant());
return body->get_state(p_state);
}
void BulletPhysicsServer3D::body_set_applied_force(RID p_body, const Vector3 &p_force) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_applied_force(p_force);
}
Vector3 BulletPhysicsServer3D::body_get_applied_force(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, Vector3());
return body->get_applied_force();
}
void BulletPhysicsServer3D::body_set_applied_torque(RID p_body, const Vector3 &p_torque) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_applied_torque(p_torque);
}
Vector3 BulletPhysicsServer3D::body_get_applied_torque(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, Vector3());
return body->get_applied_torque();
}
void BulletPhysicsServer3D::body_add_central_force(RID p_body, const Vector3 &p_force) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_central_force(p_force);
}
void BulletPhysicsServer3D::body_add_force(RID p_body, const Vector3 &p_force, const Vector3 &p_position) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_force(p_force, p_position);
}
void BulletPhysicsServer3D::body_add_torque(RID p_body, const Vector3 &p_torque) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_torque(p_torque);
}
void BulletPhysicsServer3D::body_apply_central_impulse(RID p_body, const Vector3 &p_impulse) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_central_impulse(p_impulse);
}
void BulletPhysicsServer3D::body_apply_impulse(RID p_body, const Vector3 &p_impulse, const Vector3 &p_position) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_impulse(p_impulse, p_position);
}
void BulletPhysicsServer3D::body_apply_torque_impulse(RID p_body, const Vector3 &p_impulse) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->apply_torque_impulse(p_impulse);
}
void BulletPhysicsServer3D::body_set_axis_velocity(RID p_body, const Vector3 &p_axis_velocity) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
Vector3 v = body->get_linear_velocity();
@@ -748,39 +748,39 @@ void BulletPhysicsServer3D::body_set_axis_velocity(RID p_body, const Vector3 &p_
}
void BulletPhysicsServer3D::body_set_axis_lock(RID p_body, BodyAxis p_axis, bool p_lock) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_axis_lock(p_axis, p_lock);
}
bool BulletPhysicsServer3D::body_is_axis_locked(RID p_body, BodyAxis p_axis) const {
- const RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ const RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->is_axis_locked(p_axis);
}
void BulletPhysicsServer3D::body_add_collision_exception(RID p_body, RID p_body_b) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- RigidBodyBullet *other_body = rigid_body_owner.getornull(p_body_b);
+ RigidBodyBullet *other_body = rigid_body_owner.get_or_null(p_body_b);
ERR_FAIL_COND(!other_body);
body->add_collision_exception(other_body);
}
void BulletPhysicsServer3D::body_remove_collision_exception(RID p_body, RID p_body_b) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- RigidBodyBullet *other_body = rigid_body_owner.getornull(p_body_b);
+ RigidBodyBullet *other_body = rigid_body_owner.get_or_null(p_body_b);
ERR_FAIL_COND(!other_body);
body->remove_collision_exception(other_body);
}
void BulletPhysicsServer3D::body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
for (int i = 0; i < body->get_exceptions().size(); i++) {
p_exceptions->push_back(body->get_exceptions()[i]);
@@ -788,14 +788,14 @@ void BulletPhysicsServer3D::body_get_collision_exceptions(RID p_body, List<RID>
}
void BulletPhysicsServer3D::body_set_max_contacts_reported(RID p_body, int p_contacts) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_max_collisions_detection(p_contacts);
}
int BulletPhysicsServer3D::body_get_max_contacts_reported(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_max_collisions_detection();
@@ -811,39 +811,48 @@ real_t BulletPhysicsServer3D::body_get_contacts_reported_depth_threshold(RID p_b
}
void BulletPhysicsServer3D::body_set_omit_force_integration(RID p_body, bool p_omit) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_omit_forces_integration(p_omit);
}
bool BulletPhysicsServer3D::body_is_omitting_force_integration(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, false);
return body->get_omit_forces_integration();
}
void BulletPhysicsServer3D::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_force_integration_callback(p_callable, p_udata);
}
void BulletPhysicsServer3D::body_set_ray_pickable(RID p_body, bool p_enable) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_ray_pickable(p_enable);
}
PhysicsDirectBodyState3D *BulletPhysicsServer3D::body_get_direct_state(RID p_body) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ if (!rigid_body_owner.owns(p_body)) {
+ return nullptr;
+ }
+
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, nullptr);
+
+ if (!body->get_space()) {
+ return nullptr;
+ }
+
return BulletPhysicsDirectBodyState3D::get_singleton(body);
}
bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
@@ -851,7 +860,7 @@ bool BulletPhysicsServer3D::body_test_motion(RID p_body, const Transform3D &p_fr
}
int BulletPhysicsServer3D::body_test_ray_separation(RID p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, real_t p_margin) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
ERR_FAIL_COND_V(!body->get_space(), 0);
@@ -869,19 +878,19 @@ RID BulletPhysicsServer3D::soft_body_create(bool p_init_sleeping) {
}
void BulletPhysicsServer3D::soft_body_update_rendering_server(RID p_body, RenderingServerHandler *p_rendering_server_handler) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->update_rendering_server(p_rendering_server_handler);
}
void BulletPhysicsServer3D::soft_body_set_space(RID p_body, RID p_space) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
SpaceBullet *space = nullptr;
if (p_space.is_valid()) {
- space = space_owner.getornull(p_space);
+ space = space_owner.get_or_null(p_space);
ERR_FAIL_COND(!space);
}
@@ -893,7 +902,7 @@ void BulletPhysicsServer3D::soft_body_set_space(RID p_body, RID p_space) {
}
RID BulletPhysicsServer3D::soft_body_get_space(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, RID());
SpaceBullet *space = body->get_space();
@@ -903,8 +912,8 @@ RID BulletPhysicsServer3D::soft_body_get_space(RID p_body) const {
return space->get_self();
}
-void BulletPhysicsServer3D::soft_body_set_mesh(RID p_body, const REF &p_mesh) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::soft_body_set_mesh(RID p_body, RID p_mesh) {
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_soft_mesh(p_mesh);
@@ -918,40 +927,40 @@ AABB BulletPhysicsServer::soft_body_get_bounds(RID p_body) const {
}
void BulletPhysicsServer3D::soft_body_set_collision_layer(RID p_body, uint32_t p_layer) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_collision_layer(p_layer);
}
uint32_t BulletPhysicsServer3D::soft_body_get_collision_layer(RID p_body) const {
- const SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ const SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_collision_layer();
}
void BulletPhysicsServer3D::soft_body_set_collision_mask(RID p_body, uint32_t p_mask) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_collision_mask(p_mask);
}
uint32_t BulletPhysicsServer3D::soft_body_get_collision_mask(RID p_body) const {
- const SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ const SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
return body->get_collision_mask();
}
void BulletPhysicsServer3D::soft_body_add_collision_exception(RID p_body, RID p_body_b) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- CollisionObjectBullet *other_body = rigid_body_owner.getornull(p_body_b);
+ CollisionObjectBullet *other_body = rigid_body_owner.get_or_null(p_body_b);
if (!other_body) {
- other_body = soft_body_owner.getornull(p_body_b);
+ other_body = soft_body_owner.get_or_null(p_body_b);
}
ERR_FAIL_COND(!other_body);
@@ -959,12 +968,12 @@ void BulletPhysicsServer3D::soft_body_add_collision_exception(RID p_body, RID p_
}
void BulletPhysicsServer3D::soft_body_remove_collision_exception(RID p_body, RID p_body_b) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- CollisionObjectBullet *other_body = rigid_body_owner.getornull(p_body_b);
+ CollisionObjectBullet *other_body = rigid_body_owner.get_or_null(p_body_b);
if (!other_body) {
- other_body = soft_body_owner.getornull(p_body_b);
+ other_body = soft_body_owner.get_or_null(p_body_b);
}
ERR_FAIL_COND(!other_body);
@@ -972,7 +981,7 @@ void BulletPhysicsServer3D::soft_body_remove_collision_exception(RID p_body, RID
}
void BulletPhysicsServer3D::soft_body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
for (int i = 0; i < body->get_exceptions().size(); i++) {
p_exceptions->push_back(body->get_exceptions()[i]);
@@ -991,98 +1000,98 @@ Variant BulletPhysicsServer3D::soft_body_get_state(RID p_body, BodyState p_state
}
void BulletPhysicsServer3D::soft_body_set_transform(RID p_body, const Transform3D &p_transform) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_soft_transform(p_transform);
}
void BulletPhysicsServer3D::soft_body_set_ray_pickable(RID p_body, bool p_enable) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_ray_pickable(p_enable);
}
void BulletPhysicsServer3D::soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_simulation_precision(p_simulation_precision);
}
int BulletPhysicsServer3D::soft_body_get_simulation_precision(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_simulation_precision();
}
void BulletPhysicsServer3D::soft_body_set_total_mass(RID p_body, real_t p_total_mass) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_total_mass(p_total_mass);
}
real_t BulletPhysicsServer3D::soft_body_get_total_mass(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_total_mass();
}
void BulletPhysicsServer3D::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_linear_stiffness(p_stiffness);
}
real_t BulletPhysicsServer3D::soft_body_get_linear_stiffness(RID p_body) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_linear_stiffness();
}
void BulletPhysicsServer3D::soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_pressure_coefficient(p_pressure_coefficient);
}
real_t BulletPhysicsServer3D::soft_body_get_pressure_coefficient(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_pressure_coefficient();
}
void BulletPhysicsServer3D::soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_damping_coefficient(p_damping_coefficient);
}
real_t BulletPhysicsServer3D::soft_body_get_damping_coefficient(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_damping_coefficient();
}
void BulletPhysicsServer3D::soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_drag_coefficient(p_drag_coefficient);
}
real_t BulletPhysicsServer3D::soft_body_get_drag_coefficient(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_drag_coefficient();
}
void BulletPhysicsServer3D::soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_node_position(p_point_index, p_global_position);
}
Vector3 BulletPhysicsServer3D::soft_body_get_point_global_position(RID p_body, int p_point_index) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, Vector3(0., 0., 0.));
Vector3 pos;
body->get_node_position(p_point_index, pos);
@@ -1090,25 +1099,25 @@ Vector3 BulletPhysicsServer3D::soft_body_get_point_global_position(RID p_body, i
}
void BulletPhysicsServer3D::soft_body_remove_all_pinned_points(RID p_body) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->reset_all_node_mass();
}
void BulletPhysicsServer3D::soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_node_mass(p_point_index, p_pin ? 0 : 1);
}
bool BulletPhysicsServer3D::soft_body_is_point_pinned(RID p_body, int p_point_index) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0.f);
return body->get_node_mass(p_point_index);
}
PhysicsServer3D::JointType BulletPhysicsServer3D::joint_get_type(RID p_joint) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, JOINT_PIN);
return joint->get_type();
}
@@ -1123,28 +1132,28 @@ int BulletPhysicsServer3D::joint_get_solver_priority(RID p_joint) const {
}
void BulletPhysicsServer3D::joint_disable_collisions_between_bodies(RID p_joint, const bool p_disable) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
joint->disable_collisions_between_bodies(p_disable);
}
bool BulletPhysicsServer3D::joint_is_disabled_collisions_between_bodies(RID p_joint) const {
- JointBullet *joint(joint_owner.getornull(p_joint));
+ JointBullet *joint(joint_owner.get_or_null(p_joint));
ERR_FAIL_COND_V(!joint, false);
return joint->is_disabled_collisions_between_bodies();
}
RID BulletPhysicsServer3D::joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1158,7 +1167,7 @@ RID BulletPhysicsServer3D::joint_create_pin(RID p_body_A, const Vector3 &p_local
}
void BulletPhysicsServer3D::pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1166,7 +1175,7 @@ void BulletPhysicsServer3D::pin_joint_set_param(RID p_joint, PinJointParam p_par
}
real_t BulletPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, 0);
ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, 0);
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1174,7 +1183,7 @@ real_t BulletPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_p
}
void BulletPhysicsServer3D::pin_joint_set_local_a(RID p_joint, const Vector3 &p_A) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1182,7 +1191,7 @@ void BulletPhysicsServer3D::pin_joint_set_local_a(RID p_joint, const Vector3 &p_
}
Vector3 BulletPhysicsServer3D::pin_joint_get_local_a(RID p_joint) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, Vector3());
ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, Vector3());
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1190,7 +1199,7 @@ Vector3 BulletPhysicsServer3D::pin_joint_get_local_a(RID p_joint) const {
}
void BulletPhysicsServer3D::pin_joint_set_local_b(RID p_joint, const Vector3 &p_B) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_PIN);
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1198,7 +1207,7 @@ void BulletPhysicsServer3D::pin_joint_set_local_b(RID p_joint, const Vector3 &p_
}
Vector3 BulletPhysicsServer3D::pin_joint_get_local_b(RID p_joint) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, Vector3());
ERR_FAIL_COND_V(joint->get_type() != JOINT_PIN, Vector3());
PinJointBullet *pin_joint = static_cast<PinJointBullet *>(joint);
@@ -1206,13 +1215,13 @@ Vector3 BulletPhysicsServer3D::pin_joint_get_local_b(RID p_joint) const {
}
RID BulletPhysicsServer3D::joint_create_hinge(RID p_body_A, const Transform3D &p_hinge_A, RID p_body_B, const Transform3D &p_hinge_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1226,13 +1235,13 @@ RID BulletPhysicsServer3D::joint_create_hinge(RID p_body_A, const Transform3D &p
}
RID BulletPhysicsServer3D::joint_create_hinge_simple(RID p_body_A, const Vector3 &p_pivot_A, const Vector3 &p_axis_A, RID p_body_B, const Vector3 &p_pivot_B, const Vector3 &p_axis_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1246,7 +1255,7 @@ RID BulletPhysicsServer3D::joint_create_hinge_simple(RID p_body_A, const Vector3
}
void BulletPhysicsServer3D::hinge_joint_set_param(RID p_joint, HingeJointParam p_param, real_t p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_HINGE);
HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
@@ -1254,7 +1263,7 @@ void BulletPhysicsServer3D::hinge_joint_set_param(RID p_joint, HingeJointParam p
}
real_t BulletPhysicsServer3D::hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, 0);
ERR_FAIL_COND_V(joint->get_type() != JOINT_HINGE, 0);
HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
@@ -1262,7 +1271,7 @@ real_t BulletPhysicsServer3D::hinge_joint_get_param(RID p_joint, HingeJointParam
}
void BulletPhysicsServer3D::hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_HINGE);
HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
@@ -1270,7 +1279,7 @@ void BulletPhysicsServer3D::hinge_joint_set_flag(RID p_joint, HingeJointFlag p_f
}
bool BulletPhysicsServer3D::hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, false);
ERR_FAIL_COND_V(joint->get_type() != JOINT_HINGE, false);
HingeJointBullet *hinge_joint = static_cast<HingeJointBullet *>(joint);
@@ -1278,13 +1287,13 @@ bool BulletPhysicsServer3D::hinge_joint_get_flag(RID p_joint, HingeJointFlag p_f
}
RID BulletPhysicsServer3D::joint_create_slider(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1298,7 +1307,7 @@ RID BulletPhysicsServer3D::joint_create_slider(RID p_body_A, const Transform3D &
}
void BulletPhysicsServer3D::slider_joint_set_param(RID p_joint, SliderJointParam p_param, real_t p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_SLIDER);
SliderJointBullet *slider_joint = static_cast<SliderJointBullet *>(joint);
@@ -1306,7 +1315,7 @@ void BulletPhysicsServer3D::slider_joint_set_param(RID p_joint, SliderJointParam
}
real_t BulletPhysicsServer3D::slider_joint_get_param(RID p_joint, SliderJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, 0);
ERR_FAIL_COND_V(joint->get_type() != JOINT_SLIDER, 0);
SliderJointBullet *slider_joint = static_cast<SliderJointBullet *>(joint);
@@ -1314,13 +1323,13 @@ real_t BulletPhysicsServer3D::slider_joint_get_param(RID p_joint, SliderJointPar
}
RID BulletPhysicsServer3D::joint_create_cone_twist(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1332,7 +1341,7 @@ RID BulletPhysicsServer3D::joint_create_cone_twist(RID p_body_A, const Transform
}
void BulletPhysicsServer3D::cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, real_t p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_CONE_TWIST);
ConeTwistJointBullet *coneTwist_joint = static_cast<ConeTwistJointBullet *>(joint);
@@ -1340,7 +1349,7 @@ void BulletPhysicsServer3D::cone_twist_joint_set_param(RID p_joint, ConeTwistJoi
}
real_t BulletPhysicsServer3D::cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, 0.);
ERR_FAIL_COND_V(joint->get_type() != JOINT_CONE_TWIST, 0.);
ConeTwistJointBullet *coneTwist_joint = static_cast<ConeTwistJointBullet *>(joint);
@@ -1348,13 +1357,13 @@ real_t BulletPhysicsServer3D::cone_twist_joint_get_param(RID p_joint, ConeTwistJ
}
RID BulletPhysicsServer3D::joint_create_generic_6dof(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+ RigidBodyBullet *body_A = rigid_body_owner.get_or_null(p_body_A);
ERR_FAIL_COND_V(!body_A, RID());
JointAssertSpace(body_A, "A", RID());
RigidBodyBullet *body_B = nullptr;
if (p_body_B.is_valid()) {
- body_B = rigid_body_owner.getornull(p_body_B);
+ body_B = rigid_body_owner.get_or_null(p_body_B);
JointAssertSpace(body_B, "B", RID());
JointAssertSameSpace(body_A, body_B, RID());
}
@@ -1368,7 +1377,7 @@ RID BulletPhysicsServer3D::joint_create_generic_6dof(RID p_body_A, const Transfo
}
void BulletPhysicsServer3D::generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, real_t p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
@@ -1376,7 +1385,7 @@ void BulletPhysicsServer3D::generic_6dof_joint_set_param(RID p_joint, Vector3::A
}
real_t BulletPhysicsServer3D::generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, 0);
ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, 0);
Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
@@ -1384,7 +1393,7 @@ real_t BulletPhysicsServer3D::generic_6dof_joint_get_param(RID p_joint, Vector3:
}
void BulletPhysicsServer3D::generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND(!joint);
ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
@@ -1392,7 +1401,7 @@ void BulletPhysicsServer3D::generic_6dof_joint_set_flag(RID p_joint, Vector3::Ax
}
bool BulletPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+ JointBullet *joint = joint_owner.get_or_null(p_joint);
ERR_FAIL_COND_V(!joint, false);
ERR_FAIL_COND_V(joint->get_type() != JOINT_6DOF, false);
Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
@@ -1401,17 +1410,17 @@ bool BulletPhysicsServer3D::generic_6dof_joint_get_flag(RID p_joint, Vector3::Ax
void BulletPhysicsServer3D::free(RID p_rid) {
if (shape_owner.owns(p_rid)) {
- ShapeBullet *shape = shape_owner.getornull(p_rid);
+ ShapeBullet *shape = shape_owner.get_or_null(p_rid);
// Notify the shape is configured
- for (Map<ShapeOwnerBullet *, int>::Element *element = shape->get_owners().front(); element; element = element->next()) {
- static_cast<ShapeOwnerBullet *>(element->key())->remove_shape_full(shape);
+ for (const KeyValue<ShapeOwnerBullet *, int> &element : shape->get_owners()) {
+ static_cast<ShapeOwnerBullet *>(element.key)->remove_shape_full(shape);
}
shape_owner.free(p_rid);
bulletdelete(shape);
} else if (rigid_body_owner.owns(p_rid)) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_rid);
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_rid);
body->set_space(nullptr);
@@ -1421,7 +1430,7 @@ void BulletPhysicsServer3D::free(RID p_rid) {
bulletdelete(body);
} else if (soft_body_owner.owns(p_rid)) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_rid);
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_rid);
body->set_space(nullptr);
@@ -1429,7 +1438,7 @@ void BulletPhysicsServer3D::free(RID p_rid) {
bulletdelete(body);
} else if (area_owner.owns(p_rid)) {
- AreaBullet *area = area_owner.getornull(p_rid);
+ AreaBullet *area = area_owner.get_or_null(p_rid);
area->set_space(nullptr);
@@ -1439,13 +1448,13 @@ void BulletPhysicsServer3D::free(RID p_rid) {
bulletdelete(area);
} else if (joint_owner.owns(p_rid)) {
- JointBullet *joint = joint_owner.getornull(p_rid);
+ JointBullet *joint = joint_owner.get_or_null(p_rid);
joint->destroy_internal_constraint();
joint_owner.free(p_rid);
bulletdelete(joint);
} else if (space_owner.owns(p_rid)) {
- SpaceBullet *space = space_owner.getornull(p_rid);
+ SpaceBullet *space = space_owner.get_or_null(p_rid);
space->remove_all_collision_objects();
@@ -1493,38 +1502,38 @@ int BulletPhysicsServer3D::get_process_info(ProcessInfo p_info) {
SpaceBullet *BulletPhysicsServer3D::get_space(RID p_rid) const {
ERR_FAIL_COND_V_MSG(space_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
- return space_owner.getornull(p_rid);
+ return space_owner.get_or_null(p_rid);
}
ShapeBullet *BulletPhysicsServer3D::get_shape(RID p_rid) const {
ERR_FAIL_COND_V_MSG(shape_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
- return shape_owner.getornull(p_rid);
+ return shape_owner.get_or_null(p_rid);
}
CollisionObjectBullet *BulletPhysicsServer3D::get_collision_object(RID p_object) const {
if (rigid_body_owner.owns(p_object)) {
- return rigid_body_owner.getornull(p_object);
+ return rigid_body_owner.get_or_null(p_object);
}
if (area_owner.owns(p_object)) {
- return area_owner.getornull(p_object);
+ return area_owner.get_or_null(p_object);
}
if (soft_body_owner.owns(p_object)) {
- return soft_body_owner.getornull(p_object);
+ return soft_body_owner.get_or_null(p_object);
}
ERR_FAIL_V_MSG(nullptr, "The RID is no valid.");
}
RigidCollisionObjectBullet *BulletPhysicsServer3D::get_rigid_collision_object(RID p_object) const {
if (rigid_body_owner.owns(p_object)) {
- return rigid_body_owner.getornull(p_object);
+ return rigid_body_owner.get_or_null(p_object);
}
if (area_owner.owns(p_object)) {
- return area_owner.getornull(p_object);
+ return area_owner.get_or_null(p_object);
}
ERR_FAIL_V_MSG(nullptr, "The RID is no valid.");
}
JointBullet *BulletPhysicsServer3D::get_joint(RID p_rid) const {
ERR_FAIL_COND_V_MSG(joint_owner.owns(p_rid) == false, nullptr, "The RID is not valid.");
- return joint_owner.getornull(p_rid);
+ return joint_owner.get_or_null(p_rid);
}
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 7f0934e679..94635b5bfc 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -160,8 +160,8 @@ public:
virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override;
virtual void area_set_monitorable(RID p_area, bool p_monitorable) override;
- virtual void area_set_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override;
- virtual void area_set_area_monitor_callback(RID p_area, Object *p_receiver, const StringName &p_method) override;
+ virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override;
+ virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override;
virtual void area_set_ray_pickable(RID p_area, bool p_enable) override;
/* RIGID BODY API */
@@ -265,7 +265,7 @@ public:
virtual void soft_body_set_space(RID p_body, RID p_space) override;
virtual RID soft_body_get_space(RID p_body) const override;
- virtual void soft_body_set_mesh(RID p_body, const REF &p_mesh) override;
+ virtual void soft_body_set_mesh(RID p_body, RID p_mesh) override;
virtual AABB soft_body_get_bounds(RID p_body) const override;
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index c45bd5bbc0..cbb746800d 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -93,11 +93,9 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
type(p_type) {}
CollisionObjectBullet::~CollisionObjectBullet() {
- // Remove all overlapping, notify is not required since godot take care of it
- for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
- areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
+ for (int i = 0; i < areasOverlapped.size(); i++) {
+ areasOverlapped[i]->remove_object_overlaps(this);
}
-
destroyBulletCollisionObject();
}
@@ -178,7 +176,9 @@ bool CollisionObjectBullet::is_collisions_response_enabled() {
}
void CollisionObjectBullet::notify_new_overlap(AreaBullet *p_area) {
- areasOverlapped.push_back(p_area);
+ if (areasOverlapped.find(p_area) == -1) {
+ areasOverlapped.push_back(p_area);
+ }
}
void CollisionObjectBullet::on_exit_area(AreaBullet *p_area) {
@@ -187,6 +187,7 @@ void CollisionObjectBullet::on_exit_area(AreaBullet *p_area) {
void CollisionObjectBullet::set_godot_object_flags(int flags) {
bt_collision_object->setUserIndex2(flags);
+ updated = true;
}
int CollisionObjectBullet::get_godot_object_flags() const {
@@ -220,7 +221,7 @@ const btTransform &CollisionObjectBullet::get_transform__bullet() const {
}
void CollisionObjectBullet::notify_transform_changed() {
- isTransformChanged = true;
+ updated = true;
}
RigidCollisionObjectBullet::~RigidCollisionObjectBullet() {
@@ -272,7 +273,7 @@ void RigidCollisionObjectBullet::remove_shape_full(ShapeBullet *p_shape) {
for (int i = shapes.size() - 1; 0 <= i; --i) {
if (p_shape == shapes[i].shape) {
internal_shape_destroy(i);
- shapes.remove(i);
+ shapes.remove_at(i);
}
}
reload_shapes();
@@ -281,7 +282,7 @@ void RigidCollisionObjectBullet::remove_shape_full(ShapeBullet *p_shape) {
void RigidCollisionObjectBullet::remove_shape_full(int p_index) {
ERR_FAIL_INDEX(p_index, get_shape_count());
internal_shape_destroy(p_index);
- shapes.remove(p_index);
+ shapes.remove_at(p_index);
reload_shapes();
}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
index 944ab89b87..6d2c564e44 100644
--- a/modules/bullet/collision_object_bullet.h
+++ b/modules/bullet/collision_object_bullet.h
@@ -128,7 +128,7 @@ protected:
/// New area is added when overlap with new area (AreaBullet::addOverlap), then is removed when it exit (CollisionObjectBullet::onExitArea)
/// This array is used mainly to know which area hold the pointer of this object
Vector<AreaBullet *> areasOverlapped;
- bool isTransformChanged = false;
+ bool updated = false;
public:
CollisionObjectBullet(Type p_type);
@@ -206,9 +206,9 @@ public:
Transform3D get_transform() const;
virtual void set_transform__bullet(const btTransform &p_global_transform);
virtual const btTransform &get_transform__bullet() const;
-
- bool is_transform_changed() const { return isTransformChanged; }
virtual void notify_transform_changed();
+
+ bool is_updated() const { return updated; }
};
class RigidCollisionObjectBullet : public CollisionObjectBullet, public ShapeOwnerBullet {
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 1f962772e7..edb2bcb4c7 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -107,7 +107,14 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
PhysicsDirectSpaceState3D::ShapeResult &result = m_results[count];
- result.shape = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is an odd name but contains the compound shape ID
+ // Triangle index is an odd name but contains the compound shape ID.
+ // A shape part of -1 indicates the index is a shape index and not a triangle index.
+ if (convexResult.m_localShapeInfo && convexResult.m_localShapeInfo->m_shapePart == -1) {
+ result.shape = convexResult.m_localShapeInfo->m_triangleIndex;
+ } else {
+ result.shape = 0;
+ }
+
result.rid = gObj->get_self();
result.collider_id = gObj->get_instance_id();
result.collider = result.collider_id.is_null() ? nullptr : ObjectDB::get_instance(result.collider_id);
@@ -171,11 +178,14 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0)
}
btScalar GodotClosestConvexResultCallback::addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace) {
- if (convexResult.m_localShapeInfo) {
- m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is an odd name but contains the compound shape ID
+ // Triangle index is an odd name but contains the compound shape ID.
+ // A shape part of -1 indicates the index is a shape index and not a triangle index.
+ if (convexResult.m_localShapeInfo && convexResult.m_localShapeInfo->m_shapePart == -1) {
+ m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex;
} else {
m_shapeId = 0;
}
+
return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
}
@@ -219,10 +229,22 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
CollisionObjectBullet *colObj;
if (m_self_object == colObj0Wrap->getCollisionObject()) {
colObj = static_cast<CollisionObjectBullet *>(colObj1Wrap->getCollisionObject()->getUserPointer());
- result.shape = cp.m_index1;
+ // Checking for compound shape because the index might be uninitialized otherwise.
+ // A partId of -1 indicates the index is a shape index and not a triangle index.
+ if (colObj1Wrap->getCollisionObject()->getCollisionShape()->isCompound() && cp.m_partId1 == -1) {
+ result.shape = cp.m_index1;
+ } else {
+ result.shape = 0;
+ }
} else {
colObj = static_cast<CollisionObjectBullet *>(colObj0Wrap->getCollisionObject()->getUserPointer());
- result.shape = cp.m_index0;
+ // Checking for compound shape because the index might be uninitialized otherwise.
+ // A partId of -1 indicates the index is a shape index and not a triangle index.
+ if (colObj0Wrap->getCollisionObject()->getCollisionShape()->isCompound() && cp.m_partId0 == -1) {
+ result.shape = cp.m_index0;
+ } else {
+ result.shape = 0;
+ }
}
result.collider_id = colObj->get_instance_id();
@@ -311,14 +333,26 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp
CollisionObjectBullet *colObj;
if (m_self_object == colObj0Wrap->getCollisionObject()) {
colObj = static_cast<CollisionObjectBullet *>(colObj1Wrap->getCollisionObject()->getUserPointer());
- m_result->shape = cp.m_index1;
+ // Checking for compound shape because the index might be uninitialized otherwise.
+ // A partId of -1 indicates the index is a shape index and not a triangle index.
+ if (colObj1Wrap->getCollisionObject()->getCollisionShape()->isCompound() && cp.m_partId1 == -1) {
+ m_result->shape = cp.m_index1;
+ } else {
+ m_result->shape = 0;
+ }
B_TO_G(cp.getPositionWorldOnB(), m_result->point);
B_TO_G(cp.m_normalWorldOnB, m_result->normal);
m_rest_info_bt_point = cp.getPositionWorldOnB();
m_rest_info_collision_object = colObj1Wrap->getCollisionObject();
} else {
colObj = static_cast<CollisionObjectBullet *>(colObj0Wrap->getCollisionObject()->getUserPointer());
- m_result->shape = cp.m_index0;
+ // Checking for compound shape because the index might be uninitialized otherwise.
+ // A partId of -1 indicates the index is a shape index and not a triangle index.
+ if (colObj0Wrap->getCollisionObject()->getCollisionShape()->isCompound() && cp.m_partId0 == -1) {
+ m_result->shape = cp.m_index0;
+ } else {
+ m_result->shape = 0;
+ }
B_TO_G(cp.m_normalWorldOnB * -1, m_result->normal);
m_rest_info_bt_point = cp.getPositionWorldOnA();
m_rest_info_collision_object = colObj0Wrap->getCollisionObject();
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 96a649d77a..3dfa21aec8 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -70,8 +70,10 @@ public:
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace) {
- if (rayResult.m_localShapeInfo) {
- m_shapeId = rayResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID
+ // Triangle index is an odd name but contains the compound shape ID.
+ // A shape part of -1 indicates the index is a shape index and not a triangle index.
+ if (rayResult.m_localShapeInfo && rayResult.m_localShapeInfo->m_shapePart == -1) {
+ m_shapeId = rayResult.m_localShapeInfo->m_triangleIndex;
} else {
m_shapeId = 0;
}
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 0d2cd1f5a0..4faab19539 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -303,6 +303,7 @@ RigidBodyBullet::~RigidBodyBullet() {
void RigidBodyBullet::init_kinematic_utilities() {
kinematic_utilities = memnew(KinematicUtilities(this));
+ reload_kinematic_shapes();
}
void RigidBodyBullet::destroy_kinematic_utilities() {
@@ -419,7 +420,7 @@ void RigidBodyBullet::on_collision_checker_start() {
void RigidBodyBullet::on_collision_checker_end() {
// Always true if active and not a static or kinematic body
- isTransformChanged = btBody->isActive() && !btBody->isStaticOrKinematicObject();
+ updated = btBody->isActive() && !btBody->isStaticOrKinematicObject();
}
bool RigidBodyBullet::add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const real_t &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) {
@@ -529,26 +530,23 @@ void RigidBodyBullet::set_mode(PhysicsServer3D::BodyMode p_mode) {
can_integrate_forces = false;
destroy_kinematic_utilities();
// The mode change is relevant to its mass
+ mode = p_mode;
switch (p_mode) {
case PhysicsServer3D::BODY_MODE_KINEMATIC:
- mode = PhysicsServer3D::BODY_MODE_KINEMATIC;
reload_axis_lock();
_internal_set_mass(0);
init_kinematic_utilities();
break;
case PhysicsServer3D::BODY_MODE_STATIC:
- mode = PhysicsServer3D::BODY_MODE_STATIC;
reload_axis_lock();
_internal_set_mass(0);
break;
case PhysicsServer3D::BODY_MODE_DYNAMIC:
- mode = PhysicsServer3D::BODY_MODE_DYNAMIC;
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
break;
- case PhysicsServer3D::MODE_DYNAMIC_LOCKED:
- mode = PhysicsServer3D::MODE_DYNAMIC_LOCKED;
+ case PhysicsServer3D::MODE_DYNAMIC_LINEAR:
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
@@ -721,7 +719,7 @@ bool RigidBodyBullet::is_axis_locked(PhysicsServer3D::BodyAxis p_axis) const {
void RigidBodyBullet::reload_axis_lock() {
btBody->setLinearFactor(btVector3(btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_X)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Y)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Z))));
- if (PhysicsServer3D::MODE_DYNAMIC_LOCKED == mode) {
+ if (PhysicsServer3D::MODE_DYNAMIC_LINEAR == mode) {
/// When character angular is always locked
btBody->setAngularFactor(btVector3(0., 0., 0.));
} else {
@@ -1016,7 +1014,7 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) {
// Rigidbody is dynamic if and only if mass is non Zero, otherwise static
const bool isDynamic = p_mass != 0.f;
if (isDynamic) {
- if (PhysicsServer3D::BODY_MODE_DYNAMIC != mode && PhysicsServer3D::MODE_DYNAMIC_LOCKED != mode) {
+ if (PhysicsServer3D::BODY_MODE_DYNAMIC != mode && PhysicsServer3D::MODE_DYNAMIC_LINEAR != mode) {
return;
}
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 40e785d699..ec039ba842 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -63,8 +63,8 @@ btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
}
void ShapeBullet::notifyShapeChanged() {
- for (Map<ShapeOwnerBullet *, int>::Element *E = owners.front(); E; E = E->next()) {
- ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E->key());
+ for (const KeyValue<ShapeOwnerBullet *, int> &E : owners) {
+ ShapeOwnerBullet *owner = static_cast<ShapeOwnerBullet *>(E.key);
owner->shape_changed(owner->find_shape(this));
}
}
@@ -110,7 +110,7 @@ btEmptyShape *ShapeBullet::create_shape_empty() {
return bulletnew(btEmptyShape);
}
-btStaticPlaneShape *ShapeBullet::create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant) {
+btStaticPlaneShape *ShapeBullet::create_shape_world_boundary(const btVector3 &planeNormal, btScalar planeConstant) {
return bulletnew(btStaticPlaneShape(planeNormal, planeConstant));
}
@@ -164,32 +164,32 @@ btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope
return r;
}
-/* PLANE */
+/* World boundary */
-PlaneShapeBullet::PlaneShapeBullet() :
+WorldBoundaryShapeBullet::WorldBoundaryShapeBullet() :
ShapeBullet() {}
-void PlaneShapeBullet::set_data(const Variant &p_data) {
+void WorldBoundaryShapeBullet::set_data(const Variant &p_data) {
setup(p_data);
}
-Variant PlaneShapeBullet::get_data() const {
+Variant WorldBoundaryShapeBullet::get_data() const {
return plane;
}
-PhysicsServer3D::ShapeType PlaneShapeBullet::get_type() const {
- return PhysicsServer3D::SHAPE_PLANE;
+PhysicsServer3D::ShapeType WorldBoundaryShapeBullet::get_type() const {
+ return PhysicsServer3D::SHAPE_WORLD_BOUNDARY;
}
-void PlaneShapeBullet::setup(const Plane &p_plane) {
+void WorldBoundaryShapeBullet::setup(const Plane &p_plane) {
plane = p_plane;
notifyShapeChanged();
}
-btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+btCollisionShape *WorldBoundaryShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 btPlaneNormal;
G_TO_B(plane.normal, btPlaneNormal);
- return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
+ return prepare(WorldBoundaryShapeBullet::create_shape_world_boundary(btPlaneNormal, plane.d));
}
/* Sphere */
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 5080d13d99..0822399b5e 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -81,7 +81,7 @@ public:
public:
static class btEmptyShape *create_shape_empty();
- static class btStaticPlaneShape *create_shape_plane(const btVector3 &planeNormal, btScalar planeConstant);
+ static class btStaticPlaneShape *create_shape_world_boundary(const btVector3 &planeNormal, btScalar planeConstant);
static class btSphereShape *create_shape_sphere(btScalar radius);
static class btBoxShape *create_shape_box(const btVector3 &boxHalfExtents);
static class btCapsuleShape *create_shape_capsule(btScalar radius, btScalar height);
@@ -93,11 +93,11 @@ public:
static class btRayShape *create_shape_ray(real_t p_length, bool p_slips_on_slope);
};
-class PlaneShapeBullet : public ShapeBullet {
+class WorldBoundaryShapeBullet : public ShapeBullet {
Plane plane;
public:
- PlaneShapeBullet();
+ WorldBoundaryShapeBullet();
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index bbbb0e7851..c0ffffa364 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -32,9 +32,10 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "scene/3d/soft_body_3d.h"
#include "space_bullet.h"
+#include "servers/rendering_server.h"
+
SoftBodyBullet::SoftBodyBullet() :
CollisionObjectBullet(CollisionObjectBullet::TYPE_SOFT_BODY) {}
@@ -70,7 +71,7 @@ void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering
return;
}
- /// Update visual server vertices
+ /// Update rendering server vertices
const btSoftBody::tNodeArray &nodes(bt_soft_body->m_nodes);
const int nodes_count = nodes.size();
@@ -105,24 +106,27 @@ void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering
p_rendering_server_handler->set_aabb(aabb);
}
-void SoftBodyBullet::set_soft_mesh(const Ref<Mesh> &p_mesh) {
- if (p_mesh.is_null()) {
- soft_mesh.unref();
- } else {
- soft_mesh = p_mesh;
- }
+void SoftBodyBullet::set_soft_mesh(RID p_mesh) {
+ destroy_soft_body();
+
+ soft_mesh = p_mesh;
if (soft_mesh.is_null()) {
- destroy_soft_body();
return;
}
- Array arrays = soft_mesh->surface_get_arrays(0);
- ERR_FAIL_COND(!(soft_mesh->surface_get_format(0) & RS::ARRAY_FORMAT_INDEX));
- set_trimesh_body_shape(arrays[RS::ARRAY_INDEX], arrays[RS::ARRAY_VERTEX]);
+ Array arrays = RenderingServer::get_singleton()->mesh_surface_get_arrays(soft_mesh, 0);
+ ERR_FAIL_COND(arrays.is_empty());
+
+ bool success = set_trimesh_body_shape(arrays[RS::ARRAY_INDEX], arrays[RS::ARRAY_VERTEX]);
+ if (!success) {
+ destroy_soft_body();
+ }
}
void SoftBodyBullet::destroy_soft_body() {
+ soft_mesh = RID();
+
if (!bt_soft_body) {
return;
}
@@ -187,22 +191,24 @@ void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) co
}
}
-void SoftBodyBullet::set_node_mass(int node_index, btScalar p_mass) {
+void SoftBodyBullet::set_node_mass(int p_node_index, btScalar p_mass) {
if (0 >= p_mass) {
- pin_node(node_index);
+ pin_node(p_node_index);
} else {
- unpin_node(node_index);
+ unpin_node(p_node_index);
}
if (bt_soft_body) {
- bt_soft_body->setMass(node_index, p_mass);
+ ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size());
+ bt_soft_body->setMass(p_node_index, p_mass);
}
}
-btScalar SoftBodyBullet::get_node_mass(int node_index) const {
+btScalar SoftBodyBullet::get_node_mass(int p_node_index) const {
if (bt_soft_body) {
- return bt_soft_body->getMass(node_index);
+ ERR_FAIL_INDEX_V(p_node_index, bt_soft_body->m_nodes.size(), 1);
+ return bt_soft_body->getMass(p_node_index);
} else {
- return -1 == search_node_pinned(node_index) ? 1 : 0;
+ return -1 == search_node_pinned(p_node_index) ? 1 : 0;
}
}
@@ -289,15 +295,15 @@ void SoftBodyBullet::set_drag_coefficient(real_t p_val) {
}
}
-void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices) {
- /// Assert the current soft body is destroyed
- destroy_soft_body();
+bool SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices) {
+ ERR_FAIL_COND_V(p_indices.is_empty(), false);
+ ERR_FAIL_COND_V(p_vertices.is_empty(), false);
- /// Parse visual server indices to physical indices.
- /// Merge all overlapping vertices and create a map of physical vertices to visual server
+ /// Parse rendering server indices to physical indices.
+ /// Merge all overlapping vertices and create a map of physical vertices to rendering server
{
- /// This is the map of visual server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
+ /// This is the map of rendering server indices to physics indices (So it's the inverse of idices_map), Thanks to it I don't need make a heavy search in the indices_map
Vector<int> vs_indices_to_physics_table;
{ // Map vertices
@@ -363,6 +369,8 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
bt_soft_body = btSoftBodyHelpers::CreateFromTriMesh(fake_world_info, &bt_vertices[0], &bt_triangles[0], triangles_size, false);
setup_soft_body();
}
+
+ return true;
}
void SoftBodyBullet::setup_soft_body() {
@@ -413,20 +421,28 @@ void SoftBodyBullet::setup_soft_body() {
// Set pinned nodes
for (int i = pinned_nodes.size() - 1; 0 <= i; --i) {
- bt_soft_body->setMass(pinned_nodes[i], 0);
+ const int node_index = pinned_nodes[i];
+ ERR_CONTINUE(0 > node_index || bt_soft_body->m_nodes.size() <= node_index);
+ bt_soft_body->setMass(node_index, 0);
}
}
void SoftBodyBullet::pin_node(int p_node_index) {
+ if (bt_soft_body) {
+ ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size());
+ }
if (-1 == search_node_pinned(p_node_index)) {
pinned_nodes.push_back(p_node_index);
}
}
void SoftBodyBullet::unpin_node(int p_node_index) {
+ if (bt_soft_body) {
+ ERR_FAIL_INDEX(p_node_index, bt_soft_body->m_nodes.size());
+ }
const int id = search_node_pinned(p_node_index);
if (-1 != id) {
- pinned_nodes.remove(id);
+ pinned_nodes.remove_at(id);
}
}
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index 63708b57a7..84da56ae69 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -32,7 +32,6 @@
#define SOFT_BODY_BULLET_H
#include "collision_object_bullet.h"
-#include "scene/resources/material.h" // TODO remove this please
#ifdef None
/// This is required to remove the macro None defined by x11 compiler because this word "None" is used internally by Bullet
@@ -42,7 +41,6 @@
#include "BulletSoftBody/btSoftBodyHelpers.h"
#include "collision_object_bullet.h"
-#include "scene/resources/mesh.h"
#include "servers/physics_server_3d.h"
#ifdef x11_None
@@ -64,7 +62,7 @@ private:
btSoftBody::Material *mat0 = nullptr; // This is just a copy of pointer managed by btSoftBody
bool isScratched = false;
- Ref<Mesh> soft_mesh;
+ RID soft_mesh;
int simulation_precision = 5;
real_t total_mass = 1.;
@@ -100,7 +98,7 @@ public:
void update_rendering_server(RenderingServerHandler *p_rendering_server_handler);
- void set_soft_mesh(const Ref<Mesh> &p_mesh);
+ void set_soft_mesh(RID p_mesh);
void destroy_soft_body();
// Special function. This function has bad performance
@@ -139,7 +137,7 @@ public:
_FORCE_INLINE_ real_t get_drag_coefficient() const { return drag_coefficient; }
private:
- void set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices);
+ bool set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector3> p_vertices);
void setup_soft_body();
void pin_node(int p_node_index);
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index a9a811c445..0c7e5eccf0 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -122,7 +122,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
return 0;
}
- ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, 0);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
@@ -158,7 +158,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
btVector3 bt_motion;
G_TO_B(p_motion, bt_motion);
- ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
@@ -219,7 +219,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform3D
return false;
}
- ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
@@ -251,7 +251,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform3D
}
bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
- ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->getornull(p_shape);
+ ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get_or_null(p_shape);
ERR_FAIL_COND_V(!shape, false);
btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
@@ -426,8 +426,6 @@ void SpaceBullet::set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_va
case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:
case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:
case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP:
- case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO:
- case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:
default:
WARN_PRINT("This set parameter (" + itos(p_param) + ") is ignored, the SpaceBullet doesn't support it.");
break;
@@ -442,8 +440,6 @@ real_t SpaceBullet::get_param(PhysicsServer3D::SpaceParameter p_param) {
case PhysicsServer3D::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_THRESHOLD:
case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD:
case PhysicsServer3D::SPACE_PARAM_BODY_TIME_TO_SLEEP:
- case PhysicsServer3D::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO:
- case PhysicsServer3D::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS:
default:
WARN_PRINT("The SpaceBullet doesn't support this get parameter (" + itos(p_param) + "), 0 is returned.");
return 0.f;
@@ -662,101 +658,77 @@ void SpaceBullet::destroy_world() {
}
void SpaceBullet::check_ghost_overlaps() {
- /// Algorithm support variables
- btCollisionShape *other_body_shape;
- btConvexShape *area_shape;
- btGjkPairDetector::ClosestPointInput gjk_input;
- AreaBullet *area;
- int x(-1), i(-1), y(-1), z(-1), indexOverlap(-1);
-
- /// For each areas
- for (x = areas.size() - 1; 0 <= x; --x) {
- area = areas[x];
-
- btVector3 area_scale(area->get_bt_body_scale());
-
+ // For each area
+ for (int area_idx = 0; area_idx < areas.size(); area_idx++) {
+ AreaBullet *area = areas[area_idx];
if (!area->is_monitoring()) {
continue;
}
- /// 1. Reset all states
- for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
- AreaBullet::OverlappingObjectData &otherObj = area->overlappingObjects.write[i];
- // This check prevent the overwrite of ENTER state
- // if this function is called more times before dispatchCallbacks
- if (otherObj.state != AreaBullet::OVERLAP_STATE_ENTER) {
- otherObj.state = AreaBullet::OVERLAP_STATE_DIRTY;
- }
- }
+ btGhostObject *bt_ghost = area->get_bt_ghost();
+ const btTransform &area_transform = area->get_transform__bullet();
+ const btVector3 &area_scale(area->get_bt_body_scale());
- /// 2. Check all overlapping objects using GJK
+ // Mark all current overlapping shapes dirty.
+ area->mark_all_overlaps_dirty();
- const btAlignedObjectArray<btCollisionObject *> ghostOverlaps = area->get_bt_ghost()->getOverlappingPairs();
+ // Broadphase
+ const btAlignedObjectArray<btCollisionObject *> overlapping_pairs = bt_ghost->getOverlappingPairs();
+ // Narrowphase
+ for (int pair_idx = 0; pair_idx < overlapping_pairs.size(); pair_idx++) {
+ btCollisionObject *other_bt_collision_object = overlapping_pairs[pair_idx];
+ RigidCollisionObjectBullet *other_object = static_cast<RigidCollisionObjectBullet *>(other_bt_collision_object->getUserPointer());
+ const btTransform &other_transform = other_object->get_transform__bullet();
+ const btVector3 &other_scale(other_object->get_bt_body_scale());
- // For each overlapping
- for (i = ghostOverlaps.size() - 1; 0 <= i; --i) {
- bool hasOverlap = false;
- btCollisionObject *overlapped_bt_co = ghostOverlaps[i];
- RigidCollisionObjectBullet *otherObject = static_cast<RigidCollisionObjectBullet *>(overlapped_bt_co->getUserPointer());
- btVector3 other_body_scale(otherObject->get_bt_body_scale());
-
- if (!area->is_transform_changed() && !otherObject->is_transform_changed()) {
- hasOverlap = -1 != area->find_overlapping_object(otherObject);
- goto collision_found;
+ if (!area->is_updated() && !other_object->is_updated()) {
+ area->mark_object_overlaps_inside(other_object);
+ continue;
}
- if (overlapped_bt_co->getUserIndex() == CollisionObjectBullet::TYPE_AREA) {
- if (!static_cast<AreaBullet *>(overlapped_bt_co->getUserPointer())->is_monitorable()) {
+ if (other_bt_collision_object->getUserIndex() == CollisionObjectBullet::TYPE_AREA) {
+ if (!static_cast<AreaBullet *>(other_bt_collision_object->getUserPointer())->is_monitorable()) {
continue;
}
- } else if (overlapped_bt_co->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) {
+ } else if (other_bt_collision_object->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY) {
continue;
}
// For each area shape
- for (y = area->get_shape_count() - 1; 0 <= y; --y) {
- if (!area->get_bt_shape(y)->isConvex()) {
+ for (int our_shape_id = 0; our_shape_id < area->get_shape_count(); our_shape_id++) {
+ btCollisionShape *area_shape = area->get_bt_shape(our_shape_id);
+ if (!area_shape->isConvex()) {
continue;
}
+ btConvexShape *area_convex_shape = static_cast<btConvexShape *>(area_shape);
- btTransform area_shape_treansform(area->get_bt_shape_transform(y));
- area_shape_treansform.getOrigin() *= area_scale;
-
- gjk_input.m_transformA =
- area->get_transform__bullet() *
- area_shape_treansform;
-
- area_shape = static_cast<btConvexShape *>(area->get_bt_shape(y));
+ btTransform area_shape_transform(area->get_bt_shape_transform(our_shape_id));
+ area_shape_transform.getOrigin() *= area_scale;
+ btGjkPairDetector::ClosestPointInput gjk_input;
+ gjk_input.m_transformA = area_transform * area_shape_transform;
// For each other object shape
- for (z = otherObject->get_shape_count() - 1; 0 <= z; --z) {
- other_body_shape = static_cast<btCollisionShape *>(otherObject->get_bt_shape(z));
-
- btTransform other_shape_transform(otherObject->get_bt_shape_transform(z));
- other_shape_transform.getOrigin() *= other_body_scale;
-
- gjk_input.m_transformB =
- otherObject->get_transform__bullet() *
- other_shape_transform;
+ for (int other_shape_id = 0; other_shape_id < other_object->get_shape_count(); other_shape_id++) {
+ btCollisionShape *other_shape = other_object->get_bt_shape(other_shape_id);
+ btTransform other_shape_transform(other_object->get_bt_shape_transform(other_shape_id));
+ other_shape_transform.getOrigin() *= other_scale;
+ gjk_input.m_transformB = other_transform * other_shape_transform;
- if (other_body_shape->isConvex()) {
+ if (other_shape->isConvex()) {
btPointCollector result;
btGjkPairDetector gjk_pair_detector(
- area_shape,
- static_cast<btConvexShape *>(other_body_shape),
+ area_convex_shape,
+ static_cast<btConvexShape *>(other_shape),
gjk_simplex_solver,
gjk_epa_pen_solver);
- gjk_pair_detector.getClosestPoints(gjk_input, result, nullptr);
- if (0 >= result.m_distance) {
- hasOverlap = true;
- goto collision_found;
+ gjk_pair_detector.getClosestPoints(gjk_input, result, nullptr);
+ if (result.m_distance <= 0) {
+ area->set_overlap(other_object, other_shape_id, our_shape_id);
}
-
- } else {
- btCollisionObjectWrapper obA(nullptr, area_shape, area->get_bt_ghost(), gjk_input.m_transformA, -1, y);
- btCollisionObjectWrapper obB(nullptr, other_body_shape, otherObject->get_bt_collision_object(), gjk_input.m_transformB, -1, z);
-
+ } else { // Other shape is not convex.
+ btCollisionObjectWrapper obA(nullptr, area_convex_shape, bt_ghost, gjk_input.m_transformA, -1, our_shape_id);
+ btCollisionObjectWrapper obB(nullptr, other_shape, other_bt_collision_object, gjk_input.m_transformB, -1, other_shape_id);
btCollisionAlgorithm *algorithm = dispatcher->findAlgorithm(&obA, &obB, nullptr, BT_CONTACT_POINT_ALGORITHMS);
if (!algorithm) {
@@ -765,42 +737,20 @@ void SpaceBullet::check_ghost_overlaps() {
GodotDeepPenetrationContactResultCallback contactPointResult(&obA, &obB);
algorithm->processCollision(&obA, &obB, dynamicsWorld->getDispatchInfo(), &contactPointResult);
-
algorithm->~btCollisionAlgorithm();
dispatcher->freeCollisionAlgorithm(algorithm);
if (contactPointResult.hasHit()) {
- hasOverlap = true;
- goto collision_found;
+ area->set_overlap(other_object, our_shape_id, other_shape_id);
}
}
+ } // End for each other object shape
+ } // End for each area shape
+ } // End for each overlapping pair
- } // ~For each other object shape
- } // ~For each area shape
-
- collision_found:
- if (!hasOverlap) {
- continue;
- }
-
- indexOverlap = area->find_overlapping_object(otherObject);
- if (-1 == indexOverlap) {
- // Not found
- area->add_overlap(otherObject);
- } else {
- // Found
- area->put_overlap_as_inside(indexOverlap);
- }
- }
-
- /// 3. Remove not overlapping
- for (i = area->overlappingObjects.size() - 1; 0 <= i; --i) {
- // If the overlap has DIRTY state it means that it's no more overlapping
- if (area->overlappingObjects[i].state == AreaBullet::OVERLAP_STATE_DIRTY) {
- area->put_overlap_as_exit(i);
- }
- }
- }
+ // All overlapping shapes still marked dirty must have exited.
+ area->mark_all_dirty_overlaps_as_exit();
+ } // End for each area
}
void SpaceBullet::check_body_collision() {
@@ -835,7 +785,7 @@ void SpaceBullet::check_body_collision() {
btManifoldPoint &pt = contactManifold->getContactPoint(0);
#endif
if (
- pt.getDistance() <= 0.0 ||
+ pt.getDistance() < 0.0 ||
bodyA->was_colliding(bodyB) ||
bodyB->was_colliding(bodyA)) {
Vector3 collisionWorldPosition;
@@ -947,7 +897,6 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p
if (!p_body->get_kinematic_utilities()) {
p_body->init_kinematic_utilities();
- p_body->reload_kinematic_shapes();
}
btVector3 initial_recover_motion(0, 0, 0);
@@ -1089,7 +1038,6 @@ int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform3D
if (!p_body->get_kinematic_utilities()) {
p_body->init_kinematic_utilities();
- p_body->reload_kinematic_shapes();
}
btVector3 recover_motion(0, 0, 0);
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index 2070e0e633..cf8549030d 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -76,13 +76,13 @@ private:
public:
BulletPhysicsDirectSpaceState(SpaceBullet *p_space);
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override;
- virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
- virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &r_closest_safe, real_t &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override;
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) override;
+ virtual int intersect_shape(const RID &p_shape, const Transform3D &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool cast_motion(const RID &p_shape, const Transform3D &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &r_closest_safe, real_t &r_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = nullptr) override;
/// Returns the list of contacts pairs in this order: Local contact, other body contact
- virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
- virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool collide_shape(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
+ virtual bool rest_info(RID p_shape, const Transform3D &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = UINT32_MAX, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) override;
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override;
};
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm
index 4875eb578a..6def813e5c 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_osx.mm
@@ -33,6 +33,7 @@
#include "camera_osx.h"
#include "servers/camera/camera_feed.h"
+
#import <AVFoundation/AVFoundation.h>
//////////////////////////////////////////////////////////////////////////
@@ -180,7 +181,7 @@
uint8_t *w = img_data[1].ptrw();
memcpy(w, dataCbCr, 2 * new_width * new_height);
- ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
+ ///TODO OpenGL doesn't support FORMAT_RG8, need to do some form of conversion
img[1].instantiate();
img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
}
@@ -253,10 +254,25 @@ CameraFeedOSX::~CameraFeedOSX() {
bool CameraFeedOSX::activate_feed() {
if (capture_session) {
- // already recording!
+ // Already recording!
} else {
- // start camera capture
- capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ // Start camera capture, check permission.
+ if (@available(macOS 10.14, *)) {
+ AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
+ if (status == AVAuthorizationStatusAuthorized) {
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ } else if (status == AVAuthorizationStatusNotDetermined) {
+ // Request permission.
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
+ completionHandler:^(BOOL granted) {
+ if (granted) {
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ }
+ }];
+ }
+ } else {
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ }
};
return true;
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index cb82b65307..a70e153abd 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -37,16 +37,16 @@
// Static helper functions.
inline static bool is_snapable(const Vector3 &p_point1, const Vector3 &p_point2, real_t p_distance) {
- return (p_point1 - p_point2).length_squared() < p_distance * p_distance;
+ return p_point2.distance_squared_to(p_point1) < p_distance * p_distance;
}
-inline static Vector2 interpolate_segment_uv(const Vector2 p_segement_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) {
- float segment_length = (p_segement_points[1] - p_segement_points[0]).length();
- if (p_segement_points[0].is_equal_approx(p_segement_points[1])) {
+inline static Vector2 interpolate_segment_uv(const Vector2 p_segment_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) {
+ if (p_segment_points[0].is_equal_approx(p_segment_points[1])) {
return p_uvs[0];
}
- float distance = (p_interpolation_point - p_segement_points[0]).length();
+ float segment_length = p_segment_points[0].distance_to(p_segment_points[1]);
+ float distance = p_segment_points[0].distance_to(p_interpolation_point);
float fraction = distance / segment_length;
return p_uvs[0].lerp(p_uvs[1], fraction);
@@ -156,13 +156,13 @@ inline bool is_point_in_triangle(const Vector3 &p_point, const Vector3 p_vertice
inline static bool is_triangle_degenerate(const Vector2 p_vertices[3], real_t p_vertex_snap2) {
real_t det = p_vertices[0].x * p_vertices[1].y - p_vertices[0].x * p_vertices[2].y +
- p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x +
- p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x;
+ p_vertices[0].y * p_vertices[2].x - p_vertices[0].y * p_vertices[1].x +
+ p_vertices[1].x * p_vertices[2].y - p_vertices[1].y * p_vertices[2].x;
return det < p_vertex_snap2;
}
-inline static bool are_segements_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) {
+inline static bool are_segments_parallel(const Vector2 p_segment1_points[2], const Vector2 p_segment2_points[2], float p_vertex_snap2) {
Vector2 segment1 = p_segment1_points[1] - p_segment1_points[0];
Vector2 segment2 = p_segment2_points[1] - p_segment2_points[0];
real_t segment1_length2 = segment1.dot(segment1);
@@ -258,8 +258,8 @@ void CSGBrush::build_from_faces(const Vector<Vector3> &p_vertices, const Vector<
}
materials.resize(material_map.size());
- for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) {
- materials.write[E->get()] = E->key();
+ for (const KeyValue<Ref<Material>, int> &E : material_map) {
+ materials.write[E.value] = E.key;
}
_regen_face_aabbs();
@@ -408,7 +408,7 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_b
} break;
- case OPERATION_SUBSTRACTION: {
+ case OPERATION_SUBTRACTION: {
int face_count = 0;
for (int i = 0; i < mesh_merge.faces.size(); i++) {
@@ -457,8 +457,8 @@ void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_b
// Update the list of materials.
r_merged_brush.materials.resize(mesh_merge.materials.size());
- for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
- r_merged_brush.materials.write[E->get()] = E->key();
+ for (const KeyValue<Ref<Material>, int> &E : mesh_merge.materials) {
+ r_merged_brush.materials.write[E.value] = E.key;
}
}
@@ -517,7 +517,7 @@ int CSGBrushOperation::MeshMerge::_create_bvh(FaceBVH *facebvhptr, FaceBVH **fac
int index = r_max_alloc++;
FaceBVH *_new = &facebvhptr[index];
_new->aabb = aabb;
- _new->center = aabb.position + aabb.size * 0.5;
+ _new->center = aabb.get_center();
_new->face = -1;
_new->left = left;
_new->right = right;
@@ -596,7 +596,7 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
_add_distance(intersectionsA, intersectionsB, current_face.from_b, 0);
}
} else if (ray_intersects_triangle(face_center, face_normal, current_points, CMP_EPSILON, intersection_point)) {
- real_t distance = (intersection_point - face_center).length();
+ real_t distance = face_center.distance_to(intersection_point);
_add_distance(intersectionsA, intersectionsB, current_face.from_b, distance);
}
}
@@ -678,7 +678,7 @@ void CSGBrushOperation::MeshMerge::mark_inside_faces() {
facebvh[i].aabb.position = points[faces[i].points[0]];
facebvh[i].aabb.expand_to(points[faces[i].points[1]]);
facebvh[i].aabb.expand_to(points[faces[i].points[2]]);
- facebvh[i].center = facebvh[i].aabb.position + facebvh[i].aabb.size * 0.5;
+ facebvh[i].center = facebvh[i].aabb.get_center();
facebvh[i].aabb.grow_by(vertex_snap);
facebvh[i].next = -1;
@@ -781,7 +781,7 @@ void CSGBrushOperation::MeshMerge::add_face(const Vector3 p_points[], const Vect
int CSGBrushOperation::Build2DFaces::_get_point_idx(const Vector2 &p_point) {
for (int vertex_idx = 0; vertex_idx < vertices.size(); ++vertex_idx) {
- if ((p_point - vertices[vertex_idx].point).length_squared() < vertex_snap2) {
+ if (vertices[vertex_idx].point.distance_squared_to(p_point) < vertex_snap2) {
return vertex_idx;
}
}
@@ -911,7 +911,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
vertices[outer_edge_idx[1]].point,
vertices[p_segment_indices[closest_idx]].point
};
- if (are_segements_parallel(edge1, edge2, vertex_snap2)) {
+ if (are_segments_parallel(edge1, edge2, vertex_snap2)) {
if (!degenerate_points.find(outer_edge_idx[0])) {
degenerate_points.push_back(outer_edge_idx[0]);
}
@@ -933,7 +933,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
merge_faces_idx.sort();
merge_faces_idx.reverse();
for (int i = 0; i < merge_faces_idx.size(); ++i) {
- faces.remove(merge_faces_idx[i]);
+ faces.remove_at(merge_faces_idx[i]);
}
if (degenerate_points.size() == 0) {
@@ -961,7 +961,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
// Check if point is existing face vertex.
bool existing = false;
for (int i = 0; i < 3; ++i) {
- if ((point_2D - face_vertices[i].point).length_squared() < vertex_snap2) {
+ if (face_vertices[i].point.distance_squared_to(point_2D) < vertex_snap2) {
existing = true;
break;
}
@@ -978,12 +978,12 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
};
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point_2D, edge_points);
- if ((closest_point - point_2D).length_squared() < vertex_snap2) {
+ if (point_2D.distance_squared_to(closest_point) < vertex_snap2) {
int opposite_vertex_idx = face.vertex_idx[(face_edge_idx + 2) % 3];
// If new vertex snaps to degenerate vertex, just delete this face.
if (degenerate_idx == opposite_vertex_idx) {
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
// Update index.
--face_idx;
break;
@@ -999,7 +999,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
right_face.vertex_idx[0] = opposite_vertex_idx;
right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
right_face.vertex_idx[2] = degenerate_idx;
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
faces.insert(face_idx, right_face);
faces.insert(face_idx, left_face);
@@ -1041,7 +1041,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
bool on_edge = false;
for (int edge_point_idx = 0; edge_point_idx < 2; ++edge_point_idx) {
intersection_point = Geometry2D::get_closest_point_to_segment(p_segment_points[edge_point_idx], edge_points);
- if ((intersection_point - p_segment_points[edge_point_idx]).length_squared() < vertex_snap2) {
+ if (p_segment_points[edge_point_idx].distance_squared_to(intersection_point) < vertex_snap2) {
on_edge = true;
break;
}
@@ -1050,13 +1050,13 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
// Else check if the segment intersects the edge.
if (on_edge || Geometry2D::segment_intersects_segment(p_segment_points[0], p_segment_points[1], edge_points[0], edge_points[1], &intersection_point)) {
// Check if intersection point is an edge point.
- if ((intersection_point - edge_points[0]).length_squared() < vertex_snap2 ||
- (intersection_point - edge_points[1]).length_squared() < vertex_snap2) {
+ if ((edge_points[0].distance_squared_to(intersection_point) < vertex_snap2) ||
+ (edge_points[1].distance_squared_to(intersection_point) < vertex_snap2)) {
continue;
}
// Check if edge exists, by checking if the intersecting segment is parallel to the edge.
- if (are_segements_parallel(p_segment_points, edge_points, vertex_snap2)) {
+ if (are_segments_parallel(p_segment_points, edge_points, vertex_snap2)) {
continue;
}
@@ -1070,7 +1070,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
// If new vertex snaps to opposite vertex, just delete this face.
if (new_vertex_idx == opposite_vertex_idx) {
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
// Update index.
--face_idx;
break;
@@ -1078,7 +1078,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
// If opposite point is on the segment, add its index to segment indices too.
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(vertices[opposite_vertex_idx].point, p_segment_points);
- if ((closest_point - vertices[opposite_vertex_idx].point).length_squared() < vertex_snap2) {
+ if (vertices[opposite_vertex_idx].point.distance_squared_to(closest_point) < vertex_snap2) {
_add_vertex_idx_sorted(r_segment_indices, opposite_vertex_idx);
}
@@ -1092,7 +1092,7 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
right_face.vertex_idx[0] = opposite_vertex_idx;
right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
right_face.vertex_idx[2] = new_vertex_idx;
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
faces.insert(face_idx, right_face);
faces.insert(face_idx, left_face);
@@ -1132,7 +1132,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
// Check if point is existing face vertex.
for (int i = 0; i < 3; ++i) {
- if ((p_point - face_vertices[i].point).length_squared() < vertex_snap2) {
+ if (face_vertices[i].point.distance_squared_to(p_point) < vertex_snap2) {
return face.vertex_idx[i];
}
}
@@ -1150,7 +1150,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
};
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, edge_points);
- if ((closest_point - p_point).length_squared() < vertex_snap2) {
+ if (p_point.distance_squared_to(closest_point) < vertex_snap2) {
on_edge = true;
// Add the point as a new vertex.
@@ -1162,7 +1162,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
// If new vertex snaps to opposite vertex, just delete this face.
if (new_vertex_idx == opposite_vertex_idx) {
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
// Update index.
--face_idx;
break;
@@ -1172,8 +1172,8 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
Vector2 split_edge1[2] = { vertices[new_vertex_idx].point, edge_points[0] };
Vector2 split_edge2[2] = { vertices[new_vertex_idx].point, edge_points[1] };
Vector2 new_edge[2] = { vertices[new_vertex_idx].point, vertices[opposite_vertex_idx].point };
- if (are_segements_parallel(split_edge1, new_edge, vertex_snap2) &&
- are_segements_parallel(split_edge2, new_edge, vertex_snap2)) {
+ if (are_segments_parallel(split_edge1, new_edge, vertex_snap2) &&
+ are_segments_parallel(split_edge2, new_edge, vertex_snap2)) {
break;
}
@@ -1187,7 +1187,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
right_face.vertex_idx[0] = opposite_vertex_idx;
right_face.vertex_idx[1] = face.vertex_idx[face_edge_idx];
right_face.vertex_idx[2] = new_vertex_idx;
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
faces.insert(face_idx, right_face);
faces.insert(face_idx, left_face);
@@ -1222,7 +1222,7 @@ int CSGBrushOperation::Build2DFaces::_insert_point(const Vector2 &p_point) {
new_face.vertex_idx[2] = new_vertex_idx;
faces.push_back(new_face);
}
- faces.remove(face_idx);
+ faces.remove_at(face_idx);
// No need to check other faces.
break;
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index c872860486..b1fe933268 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -67,7 +67,7 @@ struct CSGBrushOperation {
enum Operation {
OPERATION_UNION,
OPERATION_INTERSECTION,
- OPERATION_SUBSTRACTION,
+ OPERATION_SUBTRACTION,
};
void merge_brushes(Operation p_operation, const CSGBrush &p_brush_a, const CSGBrush &p_brush_b, CSGBrush &r_merged_brush, float p_vertex_snap);
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index bf11cc7f68..b9be7535dc 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -192,7 +192,7 @@ CSGBrush *CSGShape3D::_get_brush() {
bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap);
break;
case CSGShape3D::OPERATION_SUBTRACTION:
- bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap);
+ bop.merge_brushes(CSGBrushOperation::OPERATION_SUBTRACTION, *n, *nn2, *nn, snap);
break;
}
memdelete(n);
@@ -280,7 +280,7 @@ void CSGShape3D::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const
}
void CSGShape3D::_update_shape() {
- if (parent) {
+ if (parent || !is_inside_tree()) {
return;
}
@@ -572,10 +572,11 @@ void CSGShape3D::_validate_property(PropertyInfo &property) const {
bool is_collision_prefixed = property.name.begins_with("collision_");
if ((is_collision_prefixed || property.name.begins_with("use_collision")) && is_inside_tree() && !is_root_shape()) {
//hide collision if not root
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
} else if (is_collision_prefixed && !bool(get("use_collision"))) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
+ GeometryInstance3D::_validate_property(property);
}
Array CSGShape3D::get_meshes() const {
@@ -1732,6 +1733,7 @@ CSGBrush *CSGPolygon3D::_build_brush() {
int extrusion_face_count = shape_sides * 2;
int end_count = 0;
int shape_face_count = shape_faces.size() / 3;
+ real_t curve_length = 1.0;
switch (mode) {
case MODE_DEPTH:
extrusions = 1;
@@ -1744,7 +1746,12 @@ CSGBrush *CSGPolygon3D::_build_brush() {
}
break;
case MODE_PATH: {
- extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval);
+ curve_length = curve->get_baked_length();
+ if (path_interval_type == PATH_INTERVAL_DISTANCE) {
+ extrusions = MAX(1, Math::ceil(curve_length / path_interval)) + 1;
+ } else {
+ extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval);
+ }
if (!path_joined) {
end_count = 2;
extrusions -= 1;
@@ -1767,212 +1774,245 @@ CSGBrush *CSGPolygon3D::_build_brush() {
smooth.resize(face_count);
materials.resize(face_count);
invert.resize(face_count);
+ int faces_removed = 0;
- Vector3 *facesw = faces.ptrw();
- Vector2 *uvsw = uvs.ptrw();
- bool *smoothw = smooth.ptrw();
- Ref<Material> *materialsw = materials.ptrw();
- bool *invertw = invert.ptrw();
-
- int face = 0;
- Transform3D base_xform;
- Transform3D current_xform;
- Transform3D previous_xform;
- double u_step = 1.0 / extrusions;
- double v_step = 1.0 / shape_sides;
- double spin_step = Math::deg2rad(spin_degrees / spin_sides);
- double extrusion_step = 1.0 / extrusions;
- if (mode == MODE_PATH) {
- if (path_joined) {
- extrusion_step = 1.0 / (extrusions - 1);
- }
- extrusion_step *= curve->get_baked_length();
- }
+ {
+ Vector3 *facesw = faces.ptrw();
+ Vector2 *uvsw = uvs.ptrw();
+ bool *smoothw = smooth.ptrw();
+ Ref<Material> *materialsw = materials.ptrw();
+ bool *invertw = invert.ptrw();
- if (mode == MODE_PATH) {
- if (!path_local) {
- base_xform = path->get_global_transform();
+ int face = 0;
+ Transform3D base_xform;
+ Transform3D current_xform;
+ Transform3D previous_xform;
+ Transform3D previous_previous_xform;
+ double u_step = 1.0 / extrusions;
+ if (path_u_distance > 0.0) {
+ u_step *= curve_length / path_u_distance;
+ }
+ double v_step = 1.0 / shape_sides;
+ double spin_step = Math::deg2rad(spin_degrees / spin_sides);
+ double extrusion_step = 1.0 / extrusions;
+ if (mode == MODE_PATH) {
+ if (path_joined) {
+ extrusion_step = 1.0 / (extrusions - 1);
+ }
+ extrusion_step *= curve_length;
}
- Vector3 current_point = curve->interpolate_baked(0);
- Vector3 next_point = curve->interpolate_baked(extrusion_step);
- Vector3 current_up = Vector3(0, 1, 0);
- Vector3 direction = next_point - current_point;
+ if (mode == MODE_PATH) {
+ if (!path_local) {
+ base_xform = path->get_global_transform();
+ }
- if (path_joined) {
- Vector3 last_point = curve->interpolate_baked(curve->get_baked_length());
- direction = next_point - last_point;
- }
+ Vector3 current_point = curve->interpolate_baked(0);
+ Vector3 next_point = curve->interpolate_baked(extrusion_step);
+ Vector3 current_up = Vector3(0, 1, 0);
+ Vector3 direction = next_point - current_point;
+
+ if (path_joined) {
+ Vector3 last_point = curve->interpolate_baked(curve->get_baked_length());
+ direction = next_point - last_point;
+ }
- switch (path_rotation) {
- case PATH_ROTATION_POLYGON:
- direction = Vector3(0, 0, -1);
- break;
- case PATH_ROTATION_PATH:
- break;
- case PATH_ROTATION_PATH_FOLLOW:
- current_up = curve->interpolate_baked_up_vector(0);
- break;
+ switch (path_rotation) {
+ case PATH_ROTATION_POLYGON:
+ direction = Vector3(0, 0, -1);
+ break;
+ case PATH_ROTATION_PATH:
+ break;
+ case PATH_ROTATION_PATH_FOLLOW:
+ current_up = curve->interpolate_baked_up_vector(0);
+ break;
+ }
+
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = base_xform.translated(current_point) * facing;
}
- Transform3D facing = Transform3D().looking_at(direction, current_up);
- current_xform = base_xform.translated(current_point) * facing;
- }
+ // Create the mesh.
+ if (end_count > 0) {
+ // Add front end face.
+ for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
+ for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
+ // We need to reverse the rotation of the shape face vertices.
+ int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx];
+ Point2 p = shape_polygon[index];
+ Point2 uv = (p - shape_rect.position) / shape_rect.size;
+
+ // Use the left side of the bottom half of the y-inverted texture.
+ uv.x = uv.x / 2;
+ uv.y = 1 - (uv.y / 2);
+
+ facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
+ uvsw[face * 3 + face_vertex_idx] = uv;
+ }
- // Create the mesh.
- if (end_count > 0) {
- // Add front end face.
- for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
- for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
- // We need to reverse the rotation of the shape face vertices.
- int index = shape_faces[face_idx * 3 + 2 - face_vertex_idx];
- Point2 p = shape_polygon[index];
- Point2 uv = (p - shape_rect.position) / shape_rect.size;
-
- // Use the left side of the bottom half of the y-inverted texture.
- uv.x = uv.x / 2;
- uv.y = 1 - (uv.y / 2);
-
- facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
- uvsw[face * 3 + face_vertex_idx] = uv;
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_faces;
+ face++;
}
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_faces;
- face++;
}
- }
- // Add extrusion faces.
- for (int x0 = 0; x0 < extrusions; x0++) {
- previous_xform = current_xform;
-
- switch (mode) {
- case MODE_DEPTH: {
- current_xform.translate(Vector3(0, 0, -depth));
- } break;
- case MODE_SPIN: {
- current_xform.rotate(Vector3(0, 1, 0), spin_step);
- } break;
- case MODE_PATH: {
- double previous_offset = x0 * extrusion_step;
- double current_offset = (x0 + 1) * extrusion_step;
- double next_offset = (x0 + 2) * extrusion_step;
- if (x0 == extrusions - 1) {
- if (path_joined) {
- current_offset = 0;
- next_offset = extrusion_step;
+ real_t angle_simplify_dot = Math::cos(Math::deg2rad(path_simplify_angle));
+ Vector3 previous_simplify_dir = Vector3(0, 0, 0);
+ int faces_combined = 0;
+
+ // Add extrusion faces.
+ for (int x0 = 0; x0 < extrusions; x0++) {
+ previous_previous_xform = previous_xform;
+ previous_xform = current_xform;
+
+ switch (mode) {
+ case MODE_DEPTH: {
+ current_xform.translate(Vector3(0, 0, -depth));
+ } break;
+ case MODE_SPIN: {
+ current_xform.rotate(Vector3(0, 1, 0), spin_step);
+ } break;
+ case MODE_PATH: {
+ double previous_offset = x0 * extrusion_step;
+ double current_offset = (x0 + 1) * extrusion_step;
+ double next_offset = (x0 + 2) * extrusion_step;
+ if (x0 == extrusions - 1) {
+ if (path_joined) {
+ current_offset = 0;
+ next_offset = extrusion_step;
+ } else {
+ next_offset = current_offset;
+ }
+ }
+
+ Vector3 previous_point = curve->interpolate_baked(previous_offset);
+ Vector3 current_point = curve->interpolate_baked(current_offset);
+ Vector3 next_point = curve->interpolate_baked(next_offset);
+ Vector3 current_up = Vector3(0, 1, 0);
+ Vector3 direction = next_point - previous_point;
+ Vector3 current_dir = (current_point - previous_point).normalized();
+
+ // If the angles are similar, remove the previous face and replace it with this one.
+ if (path_simplify_angle > 0.0 && x0 > 0 && previous_simplify_dir.dot(current_dir) > angle_simplify_dot) {
+ faces_combined += 1;
+ previous_xform = previous_previous_xform;
+ face -= extrusion_face_count;
+ faces_removed += extrusion_face_count;
} else {
- next_offset = current_offset;
+ faces_combined = 0;
+ previous_simplify_dir = current_dir;
}
- }
- Vector3 previous_point = curve->interpolate_baked(previous_offset);
- Vector3 current_point = curve->interpolate_baked(current_offset);
- Vector3 next_point = curve->interpolate_baked(next_offset);
- Vector3 current_up = Vector3(0, 1, 0);
- Vector3 direction = next_point - previous_point;
+ switch (path_rotation) {
+ case PATH_ROTATION_POLYGON:
+ direction = Vector3(0, 0, -1);
+ break;
+ case PATH_ROTATION_PATH:
+ break;
+ case PATH_ROTATION_PATH_FOLLOW:
+ current_up = curve->interpolate_baked_up_vector(current_offset);
+ break;
+ }
- switch (path_rotation) {
- case PATH_ROTATION_POLYGON:
- direction = Vector3(0, 0, -1);
- break;
- case PATH_ROTATION_PATH:
- break;
- case PATH_ROTATION_PATH_FOLLOW:
- current_up = curve->interpolate_baked_up_vector(current_offset);
- break;
- }
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = base_xform.translated(current_point) * facing;
+ } break;
+ }
- Transform3D facing = Transform3D().looking_at(direction, current_up);
- current_xform = base_xform.translated(current_point) * facing;
- } break;
- }
+ double u0 = (x0 - faces_combined) * u_step;
+ double u1 = ((x0 + 1) * u_step);
+ if (mode == MODE_PATH && !path_continuous_u) {
+ u0 = 0.0;
+ u1 = 1.0;
+ }
- double u0 = x0 * u_step;
- double u1 = ((x0 + 1) * u_step);
- if (mode == MODE_PATH && !path_continuous_u) {
- u0 = 0.0;
- u1 = 1.0;
- }
+ for (int y0 = 0; y0 < shape_sides; y0++) {
+ int y1 = (y0 + 1) % shape_sides;
+ // Use the top half of the texture.
+ double v0 = (y0 * v_step) / 2;
+ double v1 = ((y0 + 1) * v_step) / 2;
- for (int y0 = 0; y0 < shape_sides; y0++) {
- int y1 = (y0 + 1) % shape_sides;
- // Use the top half of the texture.
- double v0 = (y0 * v_step) / 2;
- double v1 = ((y0 + 1) * v_step) / 2;
-
- Vector3 v[4] = {
- previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
- current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
- current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
- previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
- };
-
- Vector2 u[4] = {
- Vector2(u0, v0),
- Vector2(u1, v0),
- Vector2(u1, v1),
- Vector2(u0, v1),
- };
-
- // Face 1
- facesw[face * 3 + 0] = v[0];
- facesw[face * 3 + 1] = v[1];
- facesw[face * 3 + 2] = v[2];
-
- uvsw[face * 3 + 0] = u[0];
- uvsw[face * 3 + 1] = u[1];
- uvsw[face * 3 + 2] = u[2];
-
- smoothw[face] = smooth_faces;
- invertw[face] = invert_faces;
- materialsw[face] = material;
-
- face++;
-
- // Face 2
- facesw[face * 3 + 0] = v[2];
- facesw[face * 3 + 1] = v[3];
- facesw[face * 3 + 2] = v[0];
-
- uvsw[face * 3 + 0] = u[2];
- uvsw[face * 3 + 1] = u[3];
- uvsw[face * 3 + 2] = u[0];
-
- smoothw[face] = smooth_faces;
- invertw[face] = invert_faces;
- materialsw[face] = material;
-
- face++;
- }
- }
+ Vector3 v[4] = {
+ previous_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
+ current_xform.xform(Vector3(shape_polygon[y0].x, shape_polygon[y0].y, 0)),
+ current_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
+ previous_xform.xform(Vector3(shape_polygon[y1].x, shape_polygon[y1].y, 0)),
+ };
+
+ Vector2 u[4] = {
+ Vector2(u0, v0),
+ Vector2(u1, v0),
+ Vector2(u1, v1),
+ Vector2(u0, v1),
+ };
+
+ // Face 1
+ facesw[face * 3 + 0] = v[0];
+ facesw[face * 3 + 1] = v[1];
+ facesw[face * 3 + 2] = v[2];
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_faces;
+ materialsw[face] = material;
+
+ face++;
+
+ // Face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
- if (end_count > 1) {
- // Add back end face.
- for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
- for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
- int index = shape_faces[face_idx * 3 + face_vertex_idx];
- Point2 p = shape_polygon[index];
- Point2 uv = (p - shape_rect.position) / shape_rect.size;
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
- // Use the x-inverted ride side of the bottom half of the y-inverted texture.
- uv.x = 1 - uv.x / 2;
- uv.y = 1 - (uv.y / 2);
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_faces;
+ materialsw[face] = material;
- facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
- uvsw[face * 3 + face_vertex_idx] = uv;
+ face++;
}
+ }
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_faces;
- face++;
+ if (end_count > 1) {
+ // Add back end face.
+ for (int face_idx = 0; face_idx < shape_face_count; face_idx++) {
+ for (int face_vertex_idx = 0; face_vertex_idx < 3; face_vertex_idx++) {
+ int index = shape_faces[face_idx * 3 + face_vertex_idx];
+ Point2 p = shape_polygon[index];
+ Point2 uv = (p - shape_rect.position) / shape_rect.size;
+
+ // Use the x-inverted ride side of the bottom half of the y-inverted texture.
+ uv.x = 1 - uv.x / 2;
+ uv.y = 1 - (uv.y / 2);
+
+ facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
+ uvsw[face * 3 + face_vertex_idx] = uv;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_faces;
+ face++;
+ }
}
+
+ face_count -= faces_removed;
+ ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
}
- ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
+ if (faces_removed > 0) {
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+ }
brush->build_from_faces(faces, uvs, smooth, materials, invert);
@@ -2031,9 +2071,15 @@ void CSGPolygon3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon3D::set_path_node);
ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon3D::get_path_node);
+ ClassDB::bind_method(D_METHOD("set_path_interval_type", "interval_type"), &CSGPolygon3D::set_path_interval_type);
+ ClassDB::bind_method(D_METHOD("get_path_interval_type"), &CSGPolygon3D::get_path_interval_type);
+
ClassDB::bind_method(D_METHOD("set_path_interval", "interval"), &CSGPolygon3D::set_path_interval);
ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon3D::get_path_interval);
+ ClassDB::bind_method(D_METHOD("set_path_simplify_angle", "degrees"), &CSGPolygon3D::set_path_simplify_angle);
+ ClassDB::bind_method(D_METHOD("get_path_simplify_angle"), &CSGPolygon3D::get_path_simplify_angle);
+
ClassDB::bind_method(D_METHOD("set_path_rotation", "path_rotation"), &CSGPolygon3D::set_path_rotation);
ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon3D::get_path_rotation);
@@ -2043,6 +2089,9 @@ void CSGPolygon3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_path_continuous_u", "enable"), &CSGPolygon3D::set_path_continuous_u);
ClassDB::bind_method(D_METHOD("is_path_continuous_u"), &CSGPolygon3D::is_path_continuous_u);
+ ClassDB::bind_method(D_METHOD("set_path_u_distance", "distance"), &CSGPolygon3D::set_path_u_distance);
+ ClassDB::bind_method(D_METHOD("get_path_u_distance"), &CSGPolygon3D::get_path_u_distance);
+
ClassDB::bind_method(D_METHOD("set_path_joined", "enable"), &CSGPolygon3D::set_path_joined);
ClassDB::bind_method(D_METHOD("is_path_joined"), &CSGPolygon3D::is_path_joined);
@@ -2061,10 +2110,13 @@ void CSGPolygon3D::_bind_methods() {
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");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.1,1.0,0.05,exp"), "set_path_interval", "get_path_interval");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "path_interval_type", PROPERTY_HINT_ENUM, "Distance,Subdivide"), "set_path_interval_type", "get_path_interval_type");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_RANGE, "0.01,1.0,0.01,exp,or_greater"), "set_path_interval", "get_path_interval");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_simplify_angle", PROPERTY_HINT_RANGE, "0.0,180.0,0.1,exp"), "set_path_simplify_angle", "get_path_simplify_angle");
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::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");
@@ -2076,6 +2128,9 @@ void CSGPolygon3D::_bind_methods() {
BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON);
BIND_ENUM_CONSTANT(PATH_ROTATION_PATH);
BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW);
+
+ BIND_ENUM_CONSTANT(PATH_INTERVAL_DISTANCE);
+ BIND_ENUM_CONSTANT(PATH_INTERVAL_SUBDIVIDE);
}
void CSGPolygon3D::set_polygon(const Vector<Vector2> &p_polygon) {
@@ -2119,6 +2174,16 @@ bool CSGPolygon3D::is_path_continuous_u() const {
return path_continuous_u;
}
+void CSGPolygon3D::set_path_u_distance(real_t p_path_u_distance) {
+ path_u_distance = p_path_u_distance;
+ _make_dirty();
+ update_gizmos();
+}
+
+real_t CSGPolygon3D::get_path_u_distance() const {
+ return path_u_distance;
+}
+
void CSGPolygon3D::set_spin_degrees(const float p_spin_degrees) {
ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);
spin_degrees = p_spin_degrees;
@@ -2151,8 +2216,17 @@ NodePath CSGPolygon3D::get_path_node() const {
return path_node;
}
+void CSGPolygon3D::set_path_interval_type(PathIntervalType p_interval_type) {
+ path_interval_type = p_interval_type;
+ _make_dirty();
+ update_gizmos();
+}
+
+CSGPolygon3D::PathIntervalType CSGPolygon3D::get_path_interval_type() const {
+ return path_interval_type;
+}
+
void CSGPolygon3D::set_path_interval(float p_interval) {
- ERR_FAIL_COND_MSG(p_interval <= 0 || p_interval > 1, "Path interval must be greater than 0 and less than or equal to 1.0.");
path_interval = p_interval;
_make_dirty();
update_gizmos();
@@ -2162,6 +2236,16 @@ float CSGPolygon3D::get_path_interval() const {
return path_interval;
}
+void CSGPolygon3D::set_path_simplify_angle(float p_angle) {
+ path_simplify_angle = p_angle;
+ _make_dirty();
+ update_gizmos();
+}
+
+float CSGPolygon3D::get_path_simplify_angle() const {
+ return path_simplify_angle;
+}
+
void CSGPolygon3D::set_path_rotation(PathRotation p_rotation) {
path_rotation = p_rotation;
_make_dirty();
@@ -2229,10 +2313,13 @@ CSGPolygon3D::CSGPolygon3D() {
spin_degrees = 360;
spin_sides = 8;
smooth_faces = false;
+ path_interval_type = PATH_INTERVAL_DISTANCE;
path_interval = 1.0;
+ path_simplify_angle = 0.0;
path_rotation = PATH_ROTATION_PATH_FOLLOW;
path_local = false;
path_continuous_u = true;
+ path_u_distance = 1.0;
path_joined = false;
path = nullptr;
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index 5cf371665e..c85cce776b 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -336,6 +336,11 @@ public:
MODE_PATH
};
+ enum PathIntervalType {
+ PATH_INTERVAL_DISTANCE,
+ PATH_INTERVAL_SUBDIVIDE
+ };
+
enum PathRotation {
PATH_ROTATION_POLYGON,
PATH_ROTATION_PATH,
@@ -356,7 +361,9 @@ private:
int spin_sides;
NodePath path_node;
+ PathIntervalType path_interval_type;
float path_interval;
+ float path_simplify_angle;
PathRotation path_rotation;
bool path_local;
@@ -364,6 +371,7 @@ private:
bool smooth_faces;
bool path_continuous_u;
+ real_t path_u_distance;
bool path_joined;
bool _is_editable_3d_polygon() const;
@@ -396,9 +404,15 @@ public:
void set_path_node(const NodePath &p_path);
NodePath get_path_node() const;
+ void set_path_interval_type(PathIntervalType p_interval_type);
+ PathIntervalType get_path_interval_type() const;
+
void set_path_interval(float p_interval);
float get_path_interval() const;
+ void set_path_simplify_angle(float p_angle);
+ float get_path_simplify_angle() const;
+
void set_path_rotation(PathRotation p_rotation);
PathRotation get_path_rotation() const;
@@ -408,6 +422,9 @@ public:
void set_path_continuous_u(bool p_enable);
bool is_path_continuous_u() const;
+ void set_path_u_distance(real_t p_path_u_distance);
+ real_t get_path_u_distance() const;
+
void set_path_joined(bool p_enable);
bool is_path_joined() const;
@@ -422,5 +439,6 @@ public:
VARIANT_ENUM_CAST(CSGPolygon3D::Mode)
VARIANT_ENUM_CAST(CSGPolygon3D::PathRotation)
+VARIANT_ENUM_CAST(CSGPolygon3D::PathIntervalType)
#endif // CSG_SHAPE_H
diff --git a/modules/csg/doc_classes/CSGBox3D.xml b/modules/csg/doc_classes/CSGBox3D.xml
index 5bb1c4e75b..d64e58ae4d 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the box.
@@ -18,6 +16,4 @@
The box's width, height and depth.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGCombiner3D.xml b/modules/csg/doc_classes/CSGCombiner3D.xml
index b55111eee4..422c5d35b7 100644
--- a/modules/csg/doc_classes/CSGCombiner3D.xml
+++ b/modules/csg/doc_classes/CSGCombiner3D.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGCylinder3D.xml b/modules/csg/doc_classes/CSGCylinder3D.xml
index bfd2a5d5f2..40e989bfb3 100644
--- a/modules/csg/doc_classes/CSGCylinder3D.xml
+++ b/modules/csg/doc_classes/CSGCylinder3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="cone" type="bool" setter="set_cone" getter="is_cone" default="false">
If [code]true[/code] a cone is created, the [member radius] will only apply to one side.
@@ -30,6 +28,4 @@
If [code]true[/code] the normals of the cylinder are set to give a smooth effect making the cylinder seem rounded. If [code]false[/code] the cylinder will have a flat shaded look.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGMesh3D.xml b/modules/csg/doc_classes/CSGMesh3D.xml
index 5fa8427843..2810343139 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The [Material] used in drawing the CSG shape.
@@ -19,6 +17,4 @@
[b]Note:[/b] When using an [ArrayMesh], avoid meshes with vertex normals unless a flat shader is required. By default, CSGMesh will ignore the mesh's vertex normals and use a smooth shader calculated using the faces' normals. If a flat shader is required, ensure that all faces' vertex normals are parallel.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGPolygon3D.xml b/modules/csg/doc_classes/CSGPolygon3D.xml
index 5309cde956..ecbb7962d1 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
When [member mode] is [constant MODE_DEPTH], the depth of the extrusion.
@@ -26,6 +24,9 @@
<member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
When [member mode] is [constant MODE_PATH], the path interval or ratio of path points to extrusions.
</member>
+ <member name="path_interval_type" type="int" setter="set_path_interval_type" getter="get_path_interval_type" enum="CSGPolygon3D.PathIntervalType">
+ When [member mode] is [constant MODE_PATH], this will determine if the interval should be by distance ([constant PATH_INTERVAL_DISTANCE]) or subdivision fractions ([constant PATH_INTERVAL_SUBDIVIDE]).
+ </member>
<member name="path_joined" type="bool" setter="set_path_joined" getter="is_path_joined">
When [member mode] is [constant MODE_PATH], if [code]true[/code] the ends of the path are joined, by adding an extrusion between the last and first points of the path.
</member>
@@ -38,6 +39,12 @@
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation">
When [member mode] is [constant MODE_PATH], the [enum PathRotation] method used to rotate the [member polygon] as it is extruded.
</member>
+ <member name="path_simplify_angle" type="float" setter="set_path_simplify_angle" getter="get_path_simplify_angle">
+ When [member mode] is [constant MODE_PATH], extrusions that are less than this angle, will be merged together to reduce polygon count.
+ </member>
+ <member name="path_u_distance" type="float" setter="set_path_u_distance" getter="get_path_u_distance">
+ When [member mode] is [constant MODE_PATH], this is the distance along the path, in meters, the texture coordinates will tile. When set to 0, texture coordinates will match geometry exactly with no tiling.
+ </member>
<member name="polygon" type="PackedVector2Array" setter="set_polygon" getter="get_polygon" default="PackedVector2Array(0, 0, 0, 1, 1, 1, 1, 0)">
The point array that defines the 2D polygon that is extruded.
</member>
@@ -72,5 +79,11 @@
<constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
The [member polygon] shape follows the path and its rotations around the path axis.
</constant>
+ <constant name="PATH_INTERVAL_DISTANCE" value="0" enum="PathIntervalType">
+ When [member mode] is set to [constant MODE_PATH], [member path_interval] will determine the distance, in meters, each interval of the path will extrude.
+ </constant>
+ <constant name="PATH_INTERVAL_SUBDIVIDE" value="1" enum="PathIntervalType">
+ When [member mode] is set to [constant MODE_PATH], [member path_interval] will subdivide the polygons along the path.
+ </constant>
</constants>
</class>
diff --git a/modules/csg/doc_classes/CSGPrimitive3D.xml b/modules/csg/doc_classes/CSGPrimitive3D.xml
index 31b7360fac..8f4c8b9451 100644
--- a/modules/csg/doc_classes/CSGPrimitive3D.xml
+++ b/modules/csg/doc_classes/CSGPrimitive3D.xml
@@ -8,13 +8,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="invert_faces" type="bool" setter="set_invert_faces" getter="is_inverting_faces" default="false">
Invert the faces of the mesh.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index 446269f3f0..f5031064d6 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -59,10 +59,10 @@
<member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1">
The physics layers this area is in.
Collidable objects can exist in any of 32 different layers. These layers work like a tagging system, and are not visual. A collidable can use these layers to select with which objects it can collide, using the collision_mask property.
- A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
+ A contact is detected if object A is in any of the layers that object B scans, or object B is in any layer scanned by object A. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this CSG shape scans for collisions. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
+ The physics layers this CSG shape scans for collisions. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="operation" type="int" setter="set_operation" getter="get_operation" enum="CSGShape3D.Operation" default="0">
The operation that is performed on this shape. This is ignored for the first CSG child node as the operation is between this node and the previous child of this nodes parent.
diff --git a/modules/csg/doc_classes/CSGSphere3D.xml b/modules/csg/doc_classes/CSGSphere3D.xml
index 4d5b3be099..b8dfb4cf5f 100644
--- a/modules/csg/doc_classes/CSGSphere3D.xml
+++ b/modules/csg/doc_classes/CSGSphere3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the sphere.
@@ -27,6 +25,4 @@
If [code]true[/code] the normals of the sphere are set to give a smooth effect making the sphere seem rounded. If [code]false[/code] the sphere will have a flat shaded look.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/csg/doc_classes/CSGTorus3D.xml b/modules/csg/doc_classes/CSGTorus3D.xml
index abe3eab913..91ee63a4c9 100644
--- a/modules/csg/doc_classes/CSGTorus3D.xml
+++ b/modules/csg/doc_classes/CSGTorus3D.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="inner_radius" type="float" setter="set_inner_radius" getter="get_inner_radius" default="2.0">
The inner radius of the torus.
@@ -30,6 +28,4 @@
If [code]true[/code] the normals of the torus are set to give a smooth effect making the torus seem rounded. If [code]false[/code] the torus will have a flat shaded look.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/denoise/config.py b/modules/denoise/config.py
index 49a1f036ed..3aa840acb0 100644
--- a/modules/denoise/config.py
+++ b/modules/denoise/config.py
@@ -1,11 +1,18 @@
def can_build(env, platform):
# Thirdparty dependency OpenImage Denoise includes oneDNN library
- # which only supports 64-bit architectures.
+ # and the version we use only supports x86_64.
# It's also only relevant for tools build and desktop platforms,
# as doing lightmap generation and denoising on Android or HTML5
# would be a bit far-fetched.
desktop_platforms = ["linuxbsd", "osx", "windows"]
- return env["tools"] and platform in desktop_platforms and env["bits"] == "64" and env["arch"] != "arm64"
+ supported_arch = env["bits"] == "64"
+ if env["arch"] == "arm64":
+ supported_arch = False
+ if env["arch"].startswith("ppc"):
+ supported_arch = False
+ if env["arch"].startswith("rv"):
+ supported_arch = False
+ return env["tools"] and platform in desktop_platforms and supported_arch
def configure(env):
diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml
index 00469ab44c..fcdf282a7d 100644
--- a/modules/enet/doc_classes/ENetConnection.xml
+++ b/modules/enet/doc_classes/ENetConnection.xml
@@ -51,7 +51,7 @@
<argument index="3" name="data" type="int" default="0" />
<description>
Initiates a connection to a foreign [code]address[/code] using the specified [code]port[/code] and allocting the requested [code]channels[/code]. Optional [code]data[/code] can be passed during connection in the form of a 32 bit integer.
- Note: You must call either [method create_host] or [method create_host_bound] before calling this method.
+ [b]Note:[/b] You must call either [method create_host] or [method create_host_bound] before calling this method.
</description>
</method>
<method name="create_host">
@@ -121,7 +121,7 @@
<return type="Array" />
<description>
Returns the list of peers associated with this host.
- Note: This list might include some peers that are not fully connected or are still being disconnected.
+ [b]Note:[/b] This list might include some peers that are not fully connected or are still being disconnected.
</description>
</method>
<method name="pop_statistic">
@@ -136,7 +136,7 @@
<argument index="0" name="refuse" type="bool" />
<description>
Configures the DTLS server to automatically drop new connections.
- Note: This method is only relevant after calling [method dtls_server_setup].
+ [b]Note:[/b] This method is only relevant after calling [method dtls_server_setup].
</description>
</method>
<method name="service">
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
index 3a37b396a4..d2456d3360 100644
--- a/modules/enet/doc_classes/ENetMultiplayerPeer.xml
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -4,11 +4,11 @@
A MultiplayerPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library.
</brief_description>
<description>
- A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.network_peer] after being initialized as either a client, server, or mesh. Events can then be handled by connecting to [MultiplayerAPI] signals. See [ENetConnection] for more information on the ENet library wrapper.
+ A MultiplayerPeer implementation that should be passed to [member MultiplayerAPI.multiplayer_peer] after being initialized as either a client, server, or mesh. Events can then be handled by connecting to [MultiplayerAPI] signals. See [ENetConnection] for more information on the ENet library wrapper.
[b]Note:[/b] ENet only uses UDP, not TCP. When forwarding the server port to make your server accessible on the public Internet, you only need to forward the server port in UDP. You can use the [UPNP] class to try to forward the server port automatically when starting the server.
</description>
<tutorials>
- <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
+ <link title="High-level multiplayer">$DOCS_URL/tutorials/networking/high_level_multiplayer.html</link>
<link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
</tutorials>
<methods>
@@ -18,7 +18,7 @@
<argument index="1" name="host" type="ENetConnection" />
<description>
Add a new remote peer with the given [code]peer_id[/code] connected to the given [code]host[/code].
- Note: The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
+ [b]Note:[/b] The [code]host[/code] must have exactly one peer in the [constant ENetPacketPeer.STATE_CONNECTED] state.
</description>
</method>
<method name="close_connection">
@@ -44,7 +44,7 @@
<return type="int" enum="Error" />
<argument index="0" name="unique_id" type="int" />
<description>
- Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.network_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server).
+ Initialize this [MultiplayerPeer] in mesh mode. The provided [code]unique_id[/code] will be used as the local peer network unique ID once assigned as the [member MultiplayerAPI.multiplayer_peer]. In the mesh configuration you will need to set up each new peer manually using [ENetConnection] before calling [method add_mesh_peer]. While this technique is more advanced, it allows for better control over the connection process (e.g. when dealing with NAT punch-through) and for better distribution of the network load (which would otherwise be more taxing on the server).
</description>
</method>
<method name="create_server">
@@ -77,12 +77,8 @@
<member name="host" type="ENetConnection" setter="" getter="get_host">
The underlying [ENetConnection] created after [method create_client] and [method create_server].
</member>
- <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
<member name="server_relay" type="bool" setter="set_server_relay_enabled" getter="is_server_relay_enabled" default="true">
Enable or disable the server feature that notifies clients of other peers' connection/disconnection, and relays messages between them. When this option is [code]false[/code], clients won't be automatically notified of other peers and won't be able to send them packets through the server.
</member>
- <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml
index 8f0693fb01..4116ba17f2 100644
--- a/modules/enet/doc_classes/ENetPacketPeer.xml
+++ b/modules/enet/doc_classes/ENetPacketPeer.xml
@@ -6,6 +6,7 @@
<description>
A PacketPeer implementation representing a peer of an [ENetConnection].
This class cannot be instantiated directly but can be retrieved during [method ENetConnection.service] or via [method ENetConnection.get_peers].
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
<link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp
index e833264d6a..62f03b444b 100644
--- a/modules/enet/enet_connection.cpp
+++ b/modules/enet/enet_connection.cpp
@@ -117,58 +117,36 @@ Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int
return out;
}
-ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) {
- ERR_FAIL_COND_V_MSG(!host, EVENT_ERROR, "The ENetConnection instance isn't currently active.");
- ERR_FAIL_COND_V(r_event.peer.is_valid(), EVENT_ERROR);
-
- // Drop peers that have already been disconnected.
- // NOTE: Forcibly disconnected peers (i.e. peers disconnected via
- // enet_peer_disconnect*) do not trigger DISCONNECTED events.
- List<Ref<ENetPacketPeer>>::Element *E = peers.front();
- while (E) {
- if (!E->get()->is_active()) {
- peers.erase(E->get());
- }
- E = E->next();
- }
-
- ENetEvent event;
- int ret = enet_host_service(host, &event, p_timeout);
-
- if (ret < 0) {
- return EVENT_ERROR;
- } else if (ret == 0) {
- return EVENT_NONE;
- }
- switch (event.type) {
+ENetConnection::EventType ENetConnection::_parse_event(const ENetEvent &p_event, Event &r_event) {
+ switch (p_event.type) {
case ENET_EVENT_TYPE_CONNECT: {
- if (event.peer->data == nullptr) {
- Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(event.peer));
+ if (p_event.peer->data == nullptr) {
+ Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(p_event.peer));
peers.push_back(pp);
}
- r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
- r_event.data = event.data;
+ r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
+ r_event.data = p_event.data;
return EVENT_CONNECT;
} break;
case ENET_EVENT_TYPE_DISCONNECT: {
// A peer disconnected.
- if (event.peer->data != nullptr) {
- Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
+ if (p_event.peer->data != nullptr) {
+ Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
pp->_on_disconnect();
peers.erase(pp);
r_event.peer = pp;
- r_event.data = event.data;
+ r_event.data = p_event.data;
return EVENT_DISCONNECT;
}
return EVENT_ERROR;
} break;
case ENET_EVENT_TYPE_RECEIVE: {
// Packet reveived.
- if (event.peer->data != nullptr) {
- Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
- r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
- r_event.channel_id = event.channelID;
- r_event.packet = event.packet;
+ if (p_event.peer->data != nullptr) {
+ Ref<ENetPacketPeer> pp = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
+ r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)p_event.peer->data);
+ r_event.channel_id = p_event.channelID;
+ r_event.packet = p_event.packet;
return EVENT_RECEIVE;
}
return EVENT_ERROR;
@@ -180,6 +158,44 @@ ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event)
}
}
+ENetConnection::EventType ENetConnection::service(int p_timeout, Event &r_event) {
+ ERR_FAIL_COND_V_MSG(!host, EVENT_ERROR, "The ENetConnection instance isn't currently active.");
+ ERR_FAIL_COND_V(r_event.peer.is_valid(), EVENT_ERROR);
+
+ // Drop peers that have already been disconnected.
+ // NOTE: Forcibly disconnected peers (i.e. peers disconnected via
+ // enet_peer_disconnect*) do not trigger DISCONNECTED events.
+ List<Ref<ENetPacketPeer>>::Element *E = peers.front();
+ while (E) {
+ if (!E->get()->is_active()) {
+ peers.erase(E->get());
+ }
+ E = E->next();
+ }
+
+ ENetEvent event;
+ int ret = enet_host_service(host, &event, p_timeout);
+
+ if (ret < 0) {
+ return EVENT_ERROR;
+ } else if (ret == 0) {
+ return EVENT_NONE;
+ }
+ return _parse_event(event, r_event);
+}
+
+int ENetConnection::check_events(EventType &r_type, Event &r_event) {
+ ERR_FAIL_COND_V_MSG(!host, -1, "The ENetConnection instance isn't currently active.");
+ ENetEvent event;
+ int ret = enet_host_check_events(host, &event);
+ if (ret < 0) {
+ r_type = EVENT_ERROR;
+ return ret;
+ }
+ r_type = _parse_event(event, r_event);
+ return ret;
+}
+
void ENetConnection::flush() {
ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
enet_host_flush(host);
diff --git a/modules/enet/enet_connection.h b/modules/enet/enet_connection.h
index 0f7744953e..bffda81906 100644
--- a/modules/enet/enet_connection.h
+++ b/modules/enet/enet_connection.h
@@ -79,6 +79,7 @@ private:
ENetHost *host = nullptr;
List<Ref<ENetPacketPeer>> peers;
+ EventType _parse_event(const ENetEvent &p_event, Event &r_event);
Error _create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth);
Array _service(int p_timeout = 0);
void _broadcast(int p_channel, PackedByteArray p_packet, int p_flags);
@@ -110,6 +111,7 @@ public:
void destroy();
Ref<ENetPacketPeer> connect_to_host(const String &p_address, int p_port, int p_channels, int p_data = 0);
EventType service(int p_timeout, Event &r_event);
+ int check_events(EventType &r_type, Event &r_event);
void flush();
void bandwidth_limit(int p_in_bandwidth = 0, int p_out_bandwidth = 0);
void channel_limit(int p_max_channels);
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
index 38ca38385c..cb22d349f8 100644
--- a/modules/enet/enet_multiplayer_peer.cpp
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -33,22 +33,6 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
-void ENetMultiplayerPeer::set_transfer_channel(int p_channel) {
- transfer_channel = p_channel;
-}
-
-int ENetMultiplayerPeer::get_transfer_channel() const {
- return transfer_channel;
-}
-
-void ENetMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
- transfer_mode = p_mode;
-}
-
-MultiplayerPeer::TransferMode ENetMultiplayerPeer::get_transfer_mode() const {
- return transfer_mode;
-}
-
void ENetMultiplayerPeer::set_target_peer(int p_peer) {
target_peer = p_peer;
}
@@ -62,6 +46,7 @@ int ENetMultiplayerPeer::get_packet_peer() const {
Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
+ set_refuse_new_connections(false);
Ref<ENetConnection> host;
host.instantiate();
Error err = host->create_host_bound(bind_ip, p_port, p_max_clients, 0, p_max_channels > 0 ? p_max_channels + SYSCH_MAX : 0, p_out_bandwidth);
@@ -70,7 +55,6 @@ Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_ma
}
active_mode = MODE_SERVER;
- refuse_connections = false;
unique_id = 1;
connection_status = CONNECTION_CONNECTED;
hosts[0] = host;
@@ -79,6 +63,7 @@ Error ENetMultiplayerPeer::create_server(int p_port, int p_max_clients, int p_ma
Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, int p_channel_count, int p_in_bandwidth, int p_out_bandwidth, int p_local_port) {
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
+ set_refuse_new_connections(false);
Ref<ENetConnection> host;
host.instantiate();
Error err;
@@ -102,7 +87,6 @@ Error ENetMultiplayerPeer::create_client(const String &p_address, int p_port, in
// Need to wait for CONNECT event.
connection_status = CONNECTION_CONNECTING;
active_mode = MODE_CLIENT;
- refuse_connections = false;
peers[1] = peer;
hosts[0] = host;
@@ -113,7 +97,6 @@ Error ENetMultiplayerPeer::create_mesh(int p_id) {
ERR_FAIL_COND_V_MSG(p_id <= 0, ERR_INVALID_PARAMETER, "The unique ID must be greater then 0");
ERR_FAIL_COND_V_MSG(_is_active(), ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
active_mode = MODE_MESH;
- refuse_connections = false;
unique_id = p_id;
connection_status = CONNECTION_CONNECTED;
return OK;
@@ -131,32 +114,21 @@ Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
return OK;
}
-bool ENetMultiplayerPeer::_poll_server() {
- for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (!(E.value->is_active())) {
- emit_signal(SNAME("peer_disconnected"), E.value->get_meta(SNAME("_net_id")));
- peers.erase(E.key);
- }
- }
- ENetConnection::Event event;
- ENetConnection::EventType ret = hosts[0]->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- return true;
- }
- switch (ret) {
+bool ENetMultiplayerPeer::_parse_server_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
+ switch (p_type) {
case ENetConnection::EVENT_CONNECT: {
- if (refuse_connections) {
- event.peer->reset();
+ if (is_refusing_new_connections()) {
+ p_event.peer->reset();
return false;
}
// Client joined with invalid ID, probably trying to exploit us.
- if (event.data < 2 || peers.has((int)event.data)) {
- event.peer->reset();
+ if (p_event.data < 2 || peers.has((int)p_event.data)) {
+ p_event.peer->reset();
return false;
}
- int id = event.data;
- event.peer->set_meta(SNAME("_net_id"), id);
- peers[id] = event.peer;
+ int id = p_event.data;
+ p_event.peer->set_meta(SNAME("_net_id"), id);
+ peers[id] = p_event.peer;
emit_signal(SNAME("peer_connected"), id);
if (server_relay) {
@@ -165,7 +137,7 @@ bool ENetMultiplayerPeer::_poll_server() {
return false;
}
case ENetConnection::EVENT_DISCONNECT: {
- int id = event.peer->get_meta(SNAME("_net_id"));
+ int id = p_event.peer->get_meta(SNAME("_net_id"));
if (!peers.has(id)) {
// Never fully connected.
return false;
@@ -173,34 +145,34 @@ bool ENetMultiplayerPeer::_poll_server() {
emit_signal(SNAME("peer_disconnected"), id);
peers.erase(id);
- if (!server_relay) {
+ if (server_relay) {
_notify_peers(id, false);
}
return false;
}
case ENetConnection::EVENT_RECEIVE: {
- if (event.channel_id == SYSCH_CONFIG) {
- _destroy_unused(event.packet);
+ if (p_event.channel_id == SYSCH_CONFIG) {
+ _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Only server can send config messages");
} else {
- if (event.packet->dataLength < 8) {
- _destroy_unused(event.packet);
+ if (p_event.packet->dataLength < 8) {
+ _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Invalid packet size");
}
- uint32_t source = decode_uint32(&event.packet->data[0]);
- int target = decode_uint32(&event.packet->data[4]);
+ uint32_t source = decode_uint32(&p_event.packet->data[0]);
+ int target = decode_uint32(&p_event.packet->data[4]);
- uint32_t id = event.peer->get_meta(SNAME("_net_id"));
+ uint32_t id = p_event.peer->get_meta(SNAME("_net_id"));
// Someone is cheating and trying to fake the source!
if (source != id) {
- _destroy_unused(event.packet);
+ _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Someone is cheating and trying to fake the source!");
}
Packet packet;
- packet.packet = event.packet;
- packet.channel = event.channel_id;
+ packet.packet = p_event.packet;
+ packet.channel = p_event.channel_id;
packet.from = id;
// Even if relaying is disabled, these targets are valid as incoming packets.
@@ -211,9 +183,9 @@ bool ENetMultiplayerPeer::_poll_server() {
if (server_relay && target != 1) {
packet.packet->referenceCount++;
- _relay(source, target, event.channel_id, event.packet);
+ _relay(source, target, p_event.channel_id, p_event.packet);
packet.packet->referenceCount--;
- _destroy_unused(event.packet);
+ _destroy_unused(p_event.packet);
}
// Destroy packet later
}
@@ -224,23 +196,8 @@ bool ENetMultiplayerPeer::_poll_server() {
}
}
-bool ENetMultiplayerPeer::_poll_client() {
- if (peers.has(1) && !peers[1]->is_active()) {
- if (connection_status == CONNECTION_CONNECTED) {
- // Client just disconnected from server.
- emit_signal(SNAME("server_disconnected"));
- } else {
- emit_signal(SNAME("connection_failed"));
- }
- close_connection();
- return true;
- }
- ENetConnection::Event event;
- ENetConnection::EventType ret = hosts[0]->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- return true;
- }
- switch (ret) {
+bool ENetMultiplayerPeer::_parse_client_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event) {
+ switch (p_type) {
case ENetConnection::EVENT_CONNECT: {
connection_status = CONNECTION_CONNECTED;
emit_signal(SNAME("peer_connected"), 1);
@@ -258,15 +215,15 @@ bool ENetMultiplayerPeer::_poll_client() {
return true;
}
case ENetConnection::EVENT_RECEIVE: {
- if (event.channel_id == SYSCH_CONFIG) {
+ if (p_event.channel_id == SYSCH_CONFIG) {
// Config message
- if (event.packet->dataLength != 8) {
- _destroy_unused(event.packet);
+ if (p_event.packet->dataLength != 8) {
+ _destroy_unused(p_event.packet);
ERR_FAIL_V(false);
}
- int msg = decode_uint32(&event.packet->data[0]);
- int id = decode_uint32(&event.packet->data[4]);
+ int msg = decode_uint32(&p_event.packet->data[0]);
+ int id = decode_uint32(&p_event.packet->data[4]);
switch (msg) {
case SYSMSG_ADD_PEER: {
@@ -279,18 +236,18 @@ bool ENetMultiplayerPeer::_poll_client() {
emit_signal(SNAME("peer_disconnected"), id);
} break;
}
- _destroy_unused(event.packet);
+ _destroy_unused(p_event.packet);
} else {
- if (event.packet->dataLength < 8) {
- _destroy_unused(event.packet);
+ if (p_event.packet->dataLength < 8) {
+ _destroy_unused(p_event.packet);
ERR_FAIL_V_MSG(false, "Invalid packet size");
}
- uint32_t source = decode_uint32(&event.packet->data[0]);
+ uint32_t source = decode_uint32(&p_event.packet->data[0]);
Packet packet;
- packet.packet = event.packet;
+ packet.packet = p_event.packet;
packet.from = source;
- packet.channel = event.channel_id;
+ packet.channel = p_event.channel_id;
packet.packet->referenceCount++;
incoming_packets.push_back(packet);
@@ -303,61 +260,37 @@ bool ENetMultiplayerPeer::_poll_client() {
}
}
-bool ENetMultiplayerPeer::_poll_mesh() {
- for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
- if (!(E.value->is_active())) {
- emit_signal(SNAME("peer_disconnected"), E.key);
- peers.erase(E.key);
- if (hosts.has(E.key)) {
- hosts.erase(E.key);
+bool ENetMultiplayerPeer::_parse_mesh_event(ENetConnection::EventType p_type, ENetConnection::Event &p_event, int p_peer_id) {
+ switch (p_type) {
+ case ENetConnection::EVENT_CONNECT:
+ p_event.peer->reset();
+ return false;
+ case ENetConnection::EVENT_DISCONNECT:
+ if (peers.has(p_peer_id)) {
+ emit_signal(SNAME("peer_disconnected"), p_peer_id);
+ peers.erase(p_peer_id);
}
- }
- }
- bool should_stop = true;
- for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
- ENetConnection::Event event;
- ENetConnection::EventType ret = E.value->service(0, event);
- if (ret == ENetConnection::EVENT_ERROR) {
- if (peers.has(E.key)) {
- emit_signal(SNAME("peer_disconnected"), E.key);
- peers.erase(E.key);
+ hosts.erase(p_peer_id);
+ return true;
+ case ENetConnection::EVENT_RECEIVE: {
+ if (p_event.packet->dataLength < 8) {
+ _destroy_unused(p_event.packet);
+ ERR_FAIL_V_MSG(false, "Invalid packet size");
}
- hosts.erase(E.key);
- continue;
- }
- switch (ret) {
- case ENetConnection::EVENT_CONNECT:
- should_stop = false;
- event.peer->reset();
- break;
- case ENetConnection::EVENT_DISCONNECT:
- should_stop = false;
- if (peers.has(E.key)) {
- emit_signal(SNAME("peer_disconnected"), E.key);
- peers.erase(E.key);
- }
- hosts.erase(E.key);
- break;
- case ENetConnection::EVENT_RECEIVE: {
- should_stop = false;
- if (event.packet->dataLength < 8) {
- _destroy_unused(event.packet);
- ERR_CONTINUE_MSG(true, "Invalid packet size");
- }
- Packet packet;
- packet.packet = event.packet;
- packet.from = E.key;
- packet.channel = event.channel_id;
+ Packet packet;
+ packet.packet = p_event.packet;
+ packet.from = p_peer_id;
+ packet.channel = p_event.channel_id;
- packet.packet->referenceCount++;
- incoming_packets.push_back(packet);
- } break;
- default:
- break; // Nothing to do
- }
+ packet.packet->referenceCount++;
+ incoming_packets.push_back(packet);
+ return false;
+ } break;
+ default:
+ // Nothing to do
+ return true;
}
- return should_stop;
}
void ENetMultiplayerPeer::poll() {
@@ -365,26 +298,77 @@ void ENetMultiplayerPeer::poll() {
_pop_current_packet();
- while (true) {
- switch (active_mode) {
- case MODE_CLIENT:
- if (_poll_client()) {
- return;
+ switch (active_mode) {
+ case MODE_CLIENT: {
+ if (peers.has(1) && !peers[1]->is_active()) {
+ if (connection_status == CONNECTION_CONNECTED) {
+ // Client just disconnected from server.
+ emit_signal(SNAME("server_disconnected"));
+ } else {
+ emit_signal(SNAME("connection_failed"));
}
- break;
- case MODE_SERVER:
- if (_poll_server()) {
+ close_connection();
+ return;
+ }
+ ENetConnection::Event event;
+ ENetConnection::EventType ret = hosts[0]->service(0, event);
+ if (ret == ENetConnection::EVENT_ERROR) {
+ return;
+ }
+ do {
+ if (_parse_client_event(ret, event)) {
return;
}
- break;
- case MODE_MESH:
- if (_poll_mesh()) {
- return;
+ } while (hosts[0]->check_events(ret, event) > 0);
+ } break;
+ case MODE_SERVER: {
+ for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (!(E.value->is_active())) {
+ emit_signal(SNAME("peer_disconnected"), E.value->get_meta(SNAME("_net_id")));
+ peers.erase(E.key);
}
- break;
- default:
+ }
+ ENetConnection::Event event;
+ ENetConnection::EventType ret = hosts[0]->service(0, event);
+ if (ret == ENetConnection::EVENT_ERROR) {
return;
- }
+ }
+ do {
+ if (_parse_server_event(ret, event)) {
+ return;
+ }
+ } while (hosts[0]->check_events(ret, event) > 0);
+ } break;
+ case MODE_MESH: {
+ for (const KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (!(E.value->is_active())) {
+ emit_signal(SNAME("peer_disconnected"), E.key);
+ peers.erase(E.key);
+ if (hosts.has(E.key)) {
+ hosts.erase(E.key);
+ }
+ }
+ }
+ for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
+ ENetConnection::Event event;
+ ENetConnection::EventType ret = E.value->service(0, event);
+ if (ret == ENetConnection::EVENT_ERROR) {
+ if (peers.has(E.key)) {
+ emit_signal(SNAME("peer_disconnected"), E.key);
+ peers.erase(E.key);
+ }
+ hosts.erase(E.key);
+ continue;
+ }
+ do {
+ if (_parse_mesh_event(ret, event, E.key)) {
+ break; // Keep polling the others.
+ }
+ } while (E.value->check_events(ret, event) > 0);
+ }
+ } break;
+ default:
+ return;
}
}
@@ -393,7 +377,9 @@ bool ENetMultiplayerPeer::is_server() const {
}
void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
- ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active.");
+ if (!_is_active()) {
+ return;
+ }
_pop_current_packet();
@@ -421,6 +407,7 @@ void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
hosts.clear();
unique_id = 0;
connection_status = CONNECTION_DISCONNECTED;
+ set_refuse_new_connections(false);
}
int ENetMultiplayerPeer::get_available_packet_count() const {
@@ -449,25 +436,32 @@ Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size
int packet_flags = 0;
int channel = SYSCH_RELIABLE;
+ int transfer_channel = get_transfer_channel();
if (transfer_channel > 0) {
channel = SYSCH_MAX + transfer_channel - 1;
} else {
- switch (transfer_mode) {
- case TRANSFER_MODE_UNRELIABLE: {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
+ switch (get_transfer_mode()) {
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
channel = SYSCH_UNRELIABLE;
} break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED: {
- packet_flags = 0;
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ packet_flags = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT;
channel = SYSCH_UNRELIABLE;
} break;
- case TRANSFER_MODE_RELIABLE: {
+ case Multiplayer::TRANSFER_MODE_RELIABLE: {
packet_flags = ENET_PACKET_FLAG_RELIABLE;
channel = SYSCH_RELIABLE;
} break;
}
}
+#ifdef DEBUG_ENABLED
+ if ((packet_flags & ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT) && p_buffer_size + 8 > ENET_HOST_DEFAULT_MTU) {
+ WARN_PRINT_ONCE(vformat("Sending %d bytes unrealiably which is above the MTU (%d), this will result in higher packet loss", p_buffer_size + 8, ENET_HOST_DEFAULT_MTU));
+ }
+#endif
+
ENetPacket *packet = enet_packet_create(nullptr, p_buffer_size + 8, packet_flags);
encode_uint32(unique_id, &packet->data[0]); // Source ID
encode_uint32(target_peer, &packet->data[4]); // Dest ID
@@ -543,19 +537,15 @@ int ENetMultiplayerPeer::get_unique_id() const {
return unique_id;
}
-void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enable) {
- refuse_connections = p_enable;
+void ENetMultiplayerPeer::set_refuse_new_connections(bool p_enabled) {
#ifdef GODOT_ENET
if (_is_active()) {
for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
- E.value->refuse_new_connections(p_enable);
+ E.value->refuse_new_connections(p_enabled);
}
}
#endif
-}
-
-bool ENetMultiplayerPeer::is_refusing_new_connections() const {
- return refuse_connections;
+ MultiplayerPeer::set_refuse_new_connections(p_enabled);
}
void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) {
diff --git a/modules/enet/enet_multiplayer_peer.h b/modules/enet/enet_multiplayer_peer.h
index 78e280db7c..abec1e432e 100644
--- a/modules/enet/enet_multiplayer_peer.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -32,7 +32,7 @@
#define NETWORKED_MULTIPLAYER_ENET_H
#include "core/crypto/crypto.h"
-#include "core/io/multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_peer.h"
#include "enet_connection.h"
#include <enet/enet.h>
@@ -65,10 +65,7 @@ private:
uint32_t unique_id = 0;
int target_peer = 0;
- int transfer_channel = 0;
- TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
- bool refuse_connections = false;
bool server_relay = true;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
@@ -87,9 +84,9 @@ private:
Packet current_packet;
void _pop_current_packet();
- bool _poll_server();
- bool _poll_client();
- bool _poll_mesh();
+ bool _parse_server_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
+ bool _parse_client_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event);
+ bool _parse_mesh_event(ENetConnection::EventType p_event_type, ENetConnection::Event &p_event, int p_peer_id);
void _relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet);
void _notify_peers(int p_id, bool p_connected);
void _destroy_unused(ENetPacket *p_packet);
@@ -101,40 +98,31 @@ protected:
static void _bind_methods();
public:
- virtual void set_transfer_channel(int p_channel) override;
- virtual int get_transfer_channel() const override;
-
- virtual void set_transfer_mode(TransferMode p_mode) override;
- virtual TransferMode get_transfer_mode() const override;
virtual void set_target_peer(int p_peer) override;
-
virtual int get_packet_peer() const override;
- Error create_server(int p_port, int p_max_clients = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
- Error create_client(const String &p_address, int p_port, int p_channel_count = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0);
- Error create_mesh(int p_id);
- Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host);
-
- void close_connection(uint32_t wait_usec = 100);
-
- void disconnect_peer(int p_peer, bool now = false);
-
virtual void poll() override;
-
virtual bool is_server() const override;
+ // Overriden so we can instrument the DTLSServer when needed.
+ virtual void set_refuse_new_connections(bool p_enabled) override;
- virtual int get_available_packet_count() const override;
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+ virtual ConnectionStatus get_connection_status() const override;
+
+ virtual int get_unique_id() const override;
virtual int get_max_packet_size() const override;
+ virtual int get_available_packet_count() const override;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual ConnectionStatus get_connection_status() const override;
+ Error create_server(int p_port, int p_max_clients = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ Error create_client(const String &p_address, int p_port, int p_channel_count = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_local_port = 0);
+ Error create_mesh(int p_id);
+ Error add_mesh_peer(int p_id, Ref<ENetConnection> p_host);
- virtual void set_refuse_new_connections(bool p_enable) override;
- virtual bool is_refusing_new_connections() const override;
+ void close_connection(uint32_t wait_usec = 100);
- virtual int get_unique_id() const override;
+ void disconnect_peer(int p_peer, bool now = false);
void set_bind_ip(const IPAddress &p_ip);
void set_server_relay_enabled(bool p_enabled);
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index abc3c26188..aff7538fb6 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -165,9 +165,9 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_qua
if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2 || p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
- CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
- CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
} else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5 || p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp
index 86baec4244..26c9ef8d54 100644
--- a/modules/fbx/data/fbx_material.cpp
+++ b/modules/fbx/data/fbx_material.cpp
@@ -60,7 +60,7 @@ String find_file(const String &p_base, const String &p_file_to_find) {
dir.list_dir_begin();
String n = dir.get_next();
- while (n != String()) {
+ while (!n.is_empty()) {
if (n == "." || n == "..") {
n = dir.get_next();
continue;
@@ -68,7 +68,7 @@ String find_file(const String &p_base, const String &p_file_to_find) {
if (dir.current_is_dir()) {
// Don't use `path_to` or the returned path will be wrong.
const String f = find_file(p_base + "/" + n, p_file_to_find);
- if (f != "") {
+ if (!f.is_empty()) {
return f;
}
} else if (n == p_file_to_find) {
@@ -119,7 +119,7 @@ String FBXMaterial::find_texture_path_by_filename(const String p_filename, const
dir.open("res://");
dir.list_dir_begin();
String n = dir.get_next();
- while (n != String()) {
+ while (!n.is_empty()) {
if (n == "." || n == "..") {
n = dir.get_next();
continue;
@@ -136,7 +136,7 @@ String FBXMaterial::find_texture_path_by_filename(const String p_filename, const
lower_n.find("picture") >= 0) {
// Don't use `path_to` or the returned path will be wrong.
const String f = find_file(String("res://") + n, p_filename);
- if (f != "") {
+ if (!f.is_empty()) {
return f;
}
}
diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp
index dcea476275..e1eacc68b3 100644
--- a/modules/fbx/data/fbx_mesh_data.cpp
+++ b/modules/fbx/data/fbx_mesh_data.cpp
@@ -31,6 +31,7 @@
#include "fbx_mesh_data.h"
#include "core/templates/local_vector.h"
+#include "scene/resources/importer_mesh.h"
#include "scene/resources/mesh.h"
#include "scene/resources/surface_tool.h"
@@ -101,7 +102,7 @@ HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, Hash
return collection;
}
-EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) {
+ImporterMeshInstance3D *FBXMeshData::create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression) {
mesh_geometry = p_mesh_geometry;
// todo: make this just use a uint64_t FBX ID this is a copy of our original materials unfortunately.
const std::vector<const FBXDocParser::Material *> &material_lookup = model->GetMaterials();
@@ -344,7 +345,7 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
}
// Phase 6. Compose the mesh and return it.
- Ref<EditorSceneImporterMesh> mesh;
+ Ref<ImporterMesh> mesh;
mesh.instantiate();
// Add blend shape info.
@@ -356,7 +357,6 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
// Add surfaces.
- int in_mesh_surface_id = 0;
for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
SurfaceData *surface = surfaces.getptr(*surface_id);
@@ -376,11 +376,9 @@ EditorSceneImporterMeshNode3D *FBXMeshData::create_fbx_mesh(const ImportState &s
} else {
mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes);
}
-
- in_mesh_surface_id += 1;
}
- EditorSceneImporterMeshNode3D *godot_mesh = memnew(EditorSceneImporterMeshNode3D);
+ ImporterMeshInstance3D *godot_mesh = memnew(ImporterMeshInstance3D);
godot_mesh->set_mesh(mesh);
const String name = ImportUtils::FBXNodeToName(model->Name());
godot_mesh->set_name(name); // hurry up compiling >.<
diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h
index 24db4a5469..eec7f38cd6 100644
--- a/modules/fbx/data/fbx_mesh_data.h
+++ b/modules/fbx/data/fbx_mesh_data.h
@@ -35,7 +35,7 @@
#include "core/templates/local_vector.h"
#include "core/templates/ordered_hash_map.h"
#include "editor/import/resource_importer_scene.h"
-#include "editor/import/scene_importer_mesh_node_3d.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/surface_tool.h"
@@ -98,7 +98,7 @@ struct FBXMeshData : RefCounted {
// translate fbx mesh data from document context to FBX Mesh Geometry Context
bool valid_weight_indexes = false;
- EditorSceneImporterMeshNode3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression);
+ ImporterMeshInstance3D *create_fbx_mesh(const ImportState &state, const FBXDocParser::MeshGeometry *p_mesh_geometry, const FBXDocParser::Model *model, bool use_compression);
void gen_weight_info(Ref<SurfaceTool> st, int vertex_id) const;
@@ -107,7 +107,7 @@ struct FBXMeshData : RefCounted {
int max_weight_count = 0;
uint64_t armature_id = 0;
bool valid_armature_id = false;
- EditorSceneImporterMeshNode3D *godot_mesh_instance = nullptr;
+ ImporterMeshInstance3D *godot_mesh_instance = nullptr;
private:
void sanitize_vertex_weights(const ImportState &state);
diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp
index 1ac4922acf..11eed2576f 100644
--- a/modules/fbx/data/fbx_skeleton.cpp
+++ b/modules/fbx/data/fbx_skeleton.cpp
@@ -98,12 +98,19 @@ void FBXSkeleton::init_skeleton(const ImportState &state) {
ERR_FAIL_COND_MSG(skeleton->get_bone_count() != bone_count, "Not all bones got added, is the file corrupted?");
- for (Map<int, Ref<FBXBone>>::Element *bone_element = bone_map.front(); bone_element; bone_element = bone_element->next()) {
- const Ref<FBXBone> bone = bone_element->value();
- int bone_index = bone_element->key();
+ for (const KeyValue<int, Ref<FBXBone>> &bone_element : bone_map) {
+ const Ref<FBXBone> bone = bone_element.value;
+ int bone_index = bone_element.key;
print_verbose("working on bone: " + itos(bone_index) + " bone name:" + bone->bone_name);
skeleton->set_bone_rest(bone->godot_bone_id, get_unscaled_transform(bone->node->pivot_transform->LocalTransform, state.scale));
+ {
+ Transform3D base_xform = bone->node->pivot_transform->LocalTransform;
+
+ skeleton->set_bone_pose_position(bone_index, base_xform.origin);
+ skeleton->set_bone_pose_rotation(bone_index, base_xform.basis.get_rotation_quaternion());
+ skeleton->set_bone_pose_scale(bone_index, base_xform.basis.get_scale());
+ }
// lookup parent ID
if (bone->valid_parent && state.fbx_bone_map.has(bone->parent_bone_id)) {
diff --git a/modules/fbx/doc_classes/EditorSceneImporterFBX.xml b/modules/fbx/doc_classes/EditorSceneImporterFBX.xml
deleted file mode 100644
index da1a68c27c..0000000000
--- a/modules/fbx/doc_classes/EditorSceneImporterFBX.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="EditorSceneImporterFBX" inherits="EditorSceneImporter" version="4.0">
- <brief_description>
- FBX 3D asset importer.
- </brief_description>
- <description>
- This is an FBX 3D asset importer with full support for most FBX features.
- If exporting a FBX scene from Autodesk Maya, use these FBX export settings:
- [codeblock]
- - Smoothing Groups
- - Smooth Mesh
- - Triangluate (for meshes with blend shapes)
- - Bake Animation
- - Resample All
- - Deformed Models
- - Skins
- - Blend Shapes
- - Curve Filters
- - Constant Key Reducer
- - Auto Tangents Only
- - *Do not check* Constraints (as it will break the file)
- - Can check Embed Media (embeds textures into the exported FBX file)
- - Note that when importing embedded media, the texture and mesh will be a single immutable file.
- - You will have to re-export then re-import the FBX if the texture has changed.
- - Units: Centimeters
- - Up Axis: Y
- - Binary format in FBX 2017
- [/codeblock]
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index e3f36ef3e3..b11c145599 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -40,9 +40,9 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/import/resource_importer_scene.h"
-#include "editor/import/scene_importer_mesh_node_3d.h"
#include "scene/3d/bone_attachment_3d.h"
#include "scene/3d/camera_3d.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/main/node.h"
#include "scene/resources/material.h"
@@ -56,7 +56,7 @@
#include <string>
-void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const {
+void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) const {
// register FBX as the one and only format for FBX importing
const String import_setting_string = "filesystem/import/fbx/";
const String fbx_str = "fbx";
@@ -65,7 +65,7 @@ void EditorSceneImporterFBX::get_extensions(List<String> *r_extensions) const {
_register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions, true);
}
-void EditorSceneImporterFBX::_register_project_setting_import(const String generic,
+void EditorSceneFormatImporterFBX::_register_project_setting_import(const String generic,
const String import_setting_string,
const Vector<String> &exts,
List<String> *r_extensions,
@@ -79,11 +79,11 @@ void EditorSceneImporterFBX::_register_project_setting_import(const String gener
}
}
-uint32_t EditorSceneImporterFBX::get_import_flags() const {
+uint32_t EditorSceneFormatImporterFBX::get_import_flags() const {
return IMPORT_SCENE;
}
-Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
+Node3D *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
List<String> *r_missing_deps, Error *r_err) {
// done for performance when re-importing lots of files when testing importer in verbose only!
if (OS::get_singleton()->is_stdout_verbose()) {
@@ -232,7 +232,7 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
}
template <class T>
-struct EditorSceneImporterAssetImportInterpolate {
+struct EditorSceneFormatImporterAssetImportInterpolate {
T lerp(const T &a, const T &b, float c) const {
return a + (b - a) * c;
}
@@ -258,7 +258,7 @@ struct EditorSceneImporterAssetImportInterpolate {
//thank you for existing, partial specialization
template <>
-struct EditorSceneImporterAssetImportInterpolate<Quaternion> {
+struct EditorSceneFormatImporterAssetImportInterpolate<Quaternion> {
Quaternion lerp(const Quaternion &a, const Quaternion &b, float c) const {
ERR_FAIL_COND_V(!a.is_normalized(), Quaternion());
ERR_FAIL_COND_V(!b.is_normalized(), Quaternion());
@@ -282,7 +282,7 @@ struct EditorSceneImporterAssetImportInterpolate<Quaternion> {
};
template <class T>
-T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
+T EditorSceneFormatImporterFBX::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
AssetImportAnimation::Interpolation p_interp) {
//could use binary search, worth it?
int idx = -1;
@@ -293,7 +293,7 @@ T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const
idx++;
}
- EditorSceneImporterAssetImportInterpolate<T> interp;
+ EditorSceneFormatImporterAssetImportInterpolate<T> interp;
switch (p_interp) {
case AssetImportAnimation::INTERP_LINEAR: {
@@ -352,7 +352,7 @@ T EditorSceneImporterFBX::_interpolate_track(const Vector<float> &p_times, const
ERR_FAIL_V(p_values[0]);
}
-Node3D *EditorSceneImporterFBX::_generate_scene(
+Node3D *EditorSceneFormatImporterFBX::_generate_scene(
const String &p_path,
const FBXDocParser::Document *p_document,
const uint32_t p_flags,
@@ -567,8 +567,8 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// this means that the nodes from maya kLocators will be preserved as bones
// in the same rig without having to match this across skeletons and merge by detection
// we can just merge and undo any parent transforms
- for (Map<uint64_t, Ref<FBXBone>>::Element *bone_element = state.fbx_bone_map.front(); bone_element; bone_element = bone_element->next()) {
- Ref<FBXBone> bone = bone_element->value();
+ for (KeyValue<uint64_t, Ref<FBXBone>> &bone_element : state.fbx_bone_map) {
+ Ref<FBXBone> bone = bone_element.value;
Ref<FBXSkeleton> fbx_skeleton_inst;
uint64_t armature_id = bone->armature_id;
@@ -609,8 +609,8 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
}
// setup skeleton instances if required :)
- for (Map<uint64_t, Ref<FBXSkeleton>>::Element *skeleton_node = state.skeleton_map.front(); skeleton_node; skeleton_node = skeleton_node->next()) {
- Ref<FBXSkeleton> &skeleton = skeleton_node->value();
+ for (KeyValue<uint64_t, Ref<FBXSkeleton>> &skeleton_node : state.skeleton_map) {
+ Ref<FBXSkeleton> &skeleton = skeleton_node.value;
skeleton->init_skeleton(state);
ERR_CONTINUE_MSG(skeleton->fbx_node.is_null(), "invalid fbx target map, missing skeleton");
@@ -627,7 +627,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
node_element;
node_element = node_element->next()) {
Ref<FBXNode> fbx_node = node_element->get();
- EditorSceneImporterMeshNode3D *mesh_node = nullptr;
+ ImporterMeshInstance3D *mesh_node = nullptr;
Ref<FBXMeshData> mesh_data_precached;
// check for valid geometry
@@ -699,9 +699,9 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
}
}
- for (Map<uint64_t, Ref<FBXMeshData>>::Element *mesh_data = state.renderer_mesh_data.front(); mesh_data; mesh_data = mesh_data->next()) {
- const uint64_t mesh_id = mesh_data->key();
- Ref<FBXMeshData> mesh = mesh_data->value();
+ for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) {
+ const uint64_t mesh_id = mesh_data.key;
+ Ref<FBXMeshData> mesh = mesh_data.value;
const FBXDocParser::MeshGeometry *mesh_geometry = p_document->GetObject(mesh_id)->Get<FBXDocParser::MeshGeometry>();
@@ -765,10 +765,10 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
}
// mesh data iteration for populating skeleton mapping
- for (Map<uint64_t, Ref<FBXMeshData>>::Element *mesh_data = state.renderer_mesh_data.front(); mesh_data; mesh_data = mesh_data->next()) {
- Ref<FBXMeshData> mesh = mesh_data->value();
- const uint64_t mesh_id = mesh_data->key();
- EditorSceneImporterMeshNode3D *mesh_instance = mesh->godot_mesh_instance;
+ for (KeyValue<uint64_t, Ref<FBXMeshData>> &mesh_data : state.renderer_mesh_data) {
+ Ref<FBXMeshData> mesh = mesh_data.value;
+ const uint64_t mesh_id = mesh_data.key;
+ ImporterMeshInstance3D *mesh_instance = mesh->godot_mesh_instance;
const int mesh_weights = mesh->max_weight_count;
Ref<FBXSkeleton> skeleton;
const bool valid_armature = mesh->valid_armature_id;
@@ -843,7 +843,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
if (state.animation_player == nullptr) {
print_verbose("Creating animation player");
state.animation_player = memnew(AnimationPlayer);
- state.root->add_child(state.animation_player);
+ state.root->add_child(state.animation_player, true);
state.animation_player->set_owner(state.root_owner);
}
@@ -1004,16 +1004,14 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// target id, [ track name, [time index, vector] ]
//std::map<uint64_t, std::map<StringName, FBXTrack > > AnimCurveNodes;
- for (Map<uint64_t, Map<StringName, FBXTrack>>::Element *track = AnimCurveNodes.front(); track; track = track->next()) {
+ for (KeyValue<uint64_t, Map<StringName, FBXTrack>> &track : AnimCurveNodes) {
// 5 tracks
// current track index
// track count is 5
// track count is 5.
// next track id is 5.
- const uint64_t target_id = track->key();
- int track_idx = animation->add_track(Animation::TYPE_TRANSFORM3D);
+ const uint64_t target_id = track.key;
- // animation->track_set_path(track_idx, node_path);
Ref<FBXBone> bone;
// note we must not run the below code if the entry doesn't exist, it will create dummy entries which is very bad.
@@ -1037,22 +1035,21 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
// if this is a skeleton mapped track we can just set the path for the track.
// todo: implement node paths here at some
+ NodePath track_path;
if (state.fbx_bone_map.size() > 0 && state.fbx_bone_map.has(target_id)) {
if (bone->fbx_skeleton.is_valid() && bone.is_valid()) {
Ref<FBXSkeleton> fbx_skeleton = bone->fbx_skeleton;
String bone_path = state.root->get_path_to(fbx_skeleton->skeleton);
bone_path += ":" + fbx_skeleton->skeleton->get_bone_name(bone->godot_bone_id);
print_verbose("[doc] track bone path: " + bone_path);
- NodePath path = bone_path;
- animation->track_set_path(track_idx, path);
+ track_path = bone_path;
}
} else if (state.fbx_target_map.has(target_id)) {
//print_verbose("[doc] we have a valid target for a node animation");
Ref<FBXNode> target_node = state.fbx_target_map[target_id];
if (target_node.is_valid() && target_node->godot_node != nullptr) {
String node_path = state.root->get_path_to(target_node->godot_node);
- NodePath path = node_path;
- animation->track_set_path(track_idx, path);
+ track_path = node_path;
//print_verbose("[doc] node animation path: " + node_path);
}
} else {
@@ -1072,7 +1069,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
const FBXDocParser::Model *model = target_node->fbx_model;
const FBXDocParser::PropertyTable *props = dynamic_cast<const FBXDocParser::PropertyTable *>(model);
- Map<StringName, FBXTrack> &track_data = track->value();
+ Map<StringName, FBXTrack> &track_data = track.value;
FBXTrack &translation_keys = track_data[StringName("T")];
FBXTrack &rotation_keys = track_data[StringName("R")];
FBXTrack &scale_keys = track_data[StringName("S")];
@@ -1186,6 +1183,30 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
const Vector3 def_scale = scale_keys.has_default ? scale_keys.default_value : bone_rest.basis.get_scale();
print_verbose("track defaults: p(" + def_pos + ") s(" + def_scale + ") r(" + def_rot + ")");
+ int position_idx = -1;
+ if (pos_values.size()) {
+ position_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, track_path);
+ animation->track_set_imported(position_idx, true);
+ }
+
+ int rotation_idx = -1;
+ if (pos_values.size()) {
+ rotation_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, track_path);
+ animation->track_set_imported(rotation_idx, true);
+ }
+
+ int scale_idx = -1;
+ if (pos_values.size()) {
+ scale_idx = animation->get_track_count();
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, track_path);
+ animation->track_set_imported(scale_idx, true);
+ }
+
while (true) {
Vector3 pos = def_pos;
Quaternion rot = def_rot;
@@ -1206,21 +1227,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
AssetImportAnimation::INTERP_LINEAR);
}
- // node animations must also include pivots
- if (skeleton_bone >= 0) {
- Transform3D xform = Transform3D();
- xform.basis.set_quaternion_scale(rot, scale);
- xform.origin = pos;
- const Transform3D t = bone_rest.affine_inverse() * xform;
-
- // populate this again
- rot = t.basis.get_rotation_quaternion();
- rot.normalize();
- scale = t.basis.get_scale();
- pos = t.origin;
+ if (position_idx >= 0) {
+ animation->position_track_insert_key(position_idx, time, pos);
+ }
+ if (rotation_idx >= 0) {
+ animation->rotation_track_insert_key(rotation_idx, time, rot);
+ }
+ if (scale_idx >= 0) {
+ animation->scale_track_insert_key(scale_idx, time, scale);
}
-
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
if (last) {
break;
@@ -1259,15 +1274,15 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
state.fbx_target_map.clear();
state.fbx_node_list.clear();
- for (Map<uint64_t, Ref<FBXBone>>::Element *element = state.fbx_bone_map.front(); element; element = element->next()) {
- Ref<FBXBone> bone = element->value();
+ for (KeyValue<uint64_t, Ref<FBXBone>> &element : state.fbx_bone_map) {
+ Ref<FBXBone> bone = element.value;
bone->parent_bone.unref();
bone->node.unref();
bone->fbx_skeleton.unref();
}
- for (Map<uint64_t, Ref<FBXSkeleton>>::Element *element = state.skeleton_map.front(); element; element = element->next()) {
- Ref<FBXSkeleton> skel = element->value();
+ for (KeyValue<uint64_t, Ref<FBXSkeleton>> &element : state.skeleton_map) {
+ Ref<FBXSkeleton> skel = element.value;
skel->fbx_node.unref();
skel->skeleton_bones.clear();
}
@@ -1279,7 +1294,7 @@ Node3D *EditorSceneImporterFBX::_generate_scene(
return scene_root;
}
-void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone,
+void EditorSceneFormatImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone,
ImportState &state, const FBXDocParser::Document *p_doc,
uint64_t p_id) {
const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(p_id, "Model");
@@ -1368,7 +1383,7 @@ void EditorSceneImporterFBX::BuildDocumentBones(Ref<FBXBone> p_parent_bone,
}
}
-void EditorSceneImporterFBX::BuildDocumentNodes(
+void EditorSceneFormatImporterFBX::BuildDocumentNodes(
Ref<PivotTransform> parent_transform,
ImportState &state,
const FBXDocParser::Document *p_doc,
diff --git a/modules/fbx/editor_scene_importer_fbx.h b/modules/fbx/editor_scene_importer_fbx.h
index 4a3b78480b..7845e079c2 100644
--- a/modules/fbx/editor_scene_importer_fbx.h
+++ b/modules/fbx/editor_scene_importer_fbx.h
@@ -57,9 +57,9 @@
#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
-class EditorSceneImporterFBX : public EditorSceneImporter {
+class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter {
private:
- GDCLASS(EditorSceneImporterFBX, EditorSceneImporter);
+ GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter);
struct AssetImportAnimation {
enum Interpolation {
@@ -122,8 +122,8 @@ private:
void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
public:
- EditorSceneImporterFBX() {}
- ~EditorSceneImporterFBX() {}
+ EditorSceneFormatImporterFBX() {}
+ ~EditorSceneFormatImporterFBX() {}
virtual void get_extensions(List<String> *r_extensions) const override;
virtual uint32_t get_import_flags() const override;
diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
index 1eee10b251..d6abcbb00a 100644
--- a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
+++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
@@ -82,46 +82,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdint.h>
namespace FBXDocParser {
-//enum Flag
-//{
-// e_unknown_0 = 1 << 0,
-// e_unknown_1 = 1 << 1,
-// e_unknown_2 = 1 << 2,
-// e_unknown_3 = 1 << 3,
-// e_unknown_4 = 1 << 4,
-// e_unknown_5 = 1 << 5,
-// e_unknown_6 = 1 << 6,
-// e_unknown_7 = 1 << 7,
-// e_unknown_8 = 1 << 8,
-// e_unknown_9 = 1 << 9,
-// e_unknown_10 = 1 << 10,
-// e_unknown_11 = 1 << 11,
-// e_unknown_12 = 1 << 12,
-// e_unknown_13 = 1 << 13,
-// e_unknown_14 = 1 << 14,
-// e_unknown_15 = 1 << 15,
-// e_unknown_16 = 1 << 16,
-// e_unknown_17 = 1 << 17,
-// e_unknown_18 = 1 << 18,
-// e_unknown_19 = 1 << 19,
-// e_unknown_20 = 1 << 20,
-// e_unknown_21 = 1 << 21,
-// e_unknown_22 = 1 << 22,
-// e_unknown_23 = 1 << 23,
-// e_flag_field_size_64_bit = 1 << 24, // Not sure what is
-// e_unknown_25 = 1 << 25,
-// e_unknown_26 = 1 << 26,
-// e_unknown_27 = 1 << 27,
-// e_unknown_28 = 1 << 28,
-// e_unknown_29 = 1 << 29,
-// e_unknown_30 = 1 << 30,
-// e_unknown_31 = 1 << 31
-//};
-//
-//bool check_flag(uint32_t flags, Flag to_check)
-//{
-// return (flags & to_check) != 0;
-//}
// ------------------------------------------------------------------------------------------------
Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) :
sbegin(sbegin),
@@ -458,12 +418,6 @@ void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length,
//TokenizeError("file is too short",0);
}
- //uint32_t offset = 0x15;
- /* const char* cursor = input + 0x15;
- const uint32_t flags = ReadWord(input, cursor, input + length);
- const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
- const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/
-
if (strncmp(input, "Kaydara FBX Binary", 18)) {
TokenizeError("magic bytes not found", 0);
}
diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h
index 641d6d351e..611bf22d3b 100644
--- a/modules/fbx/fbx_parser/FBXCommon.h
+++ b/modules/fbx/fbx_parser/FBXCommon.h
@@ -70,8 +70,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file FBXCommon.h
-* Some useful constants and enums for dealing with FBX files.
-*/
+ * Some useful constants and enums for dealing with FBX files.
+ */
#ifndef FBX_COMMON_H
#define FBX_COMMON_H
diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h
index 885ff8fca4..539d633331 100644
--- a/modules/fbx/fbx_parser/FBXDocument.h
+++ b/modules/fbx/fbx_parser/FBXDocument.h
@@ -706,13 +706,13 @@ public:
virtual ~AnimationCurve();
/** get list of keyframe positions (time).
- * Invariant: |GetKeys()| > 0 */
+ * Invariant: |GetKeys()| > 0 */
const KeyTimeList &GetKeys() const {
return keys;
}
/** get list of keyframe values.
- * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
+ * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
const KeyValueList &GetValues() const {
return values;
}
@@ -750,8 +750,8 @@ typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr;
class AnimationCurveNode : public Object {
public:
/* the optional white list specifies a list of property names for which the caller
- wants animations for. If the curve node does not match one of these, std::range_error
- will be thrown. */
+ wants animations for. If the curve node does not match one of these, std::range_error
+ will be thrown. */
AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc,
const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
@@ -760,8 +760,8 @@ public:
const AnimationMap &Curves() const;
/** Object the curve is assigned to, this can be nullptr if the
- * target object has no DOM representation or could not
- * be read for other reasons.*/
+ * target object has no DOM representation or could not
+ * be read for other reasons.*/
Object *Target() const {
return target;
}
@@ -799,8 +799,8 @@ public:
virtual ~AnimationLayer();
/* the optional white list specifies a list of property names for which the caller
- wants animations for. Curves not matching this list will not be added to the
- animation layer. */
+ wants animations for. Curves not matching this list will not be added to the
+ animation layer. */
const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
private:
@@ -891,15 +891,15 @@ public:
virtual ~Cluster();
/** get the list of deformer weights associated with this cluster.
- * Use #GetIndices() to get the associated vertices. Both arrays
- * have the same size (and may also be empty). */
+ * Use #GetIndices() to get the associated vertices. Both arrays
+ * have the same size (and may also be empty). */
const std::vector<float> &GetWeights() const {
return weights;
}
/** get indices into the vertex data of the geometry associated
- * with this cluster. Use #GetWeights() to get the associated weights.
- * Both arrays have the same size (and may also be empty). */
+ * with this cluster. Use #GetWeights() to get the associated weights.
+ * Both arrays have the same size (and may also be empty). */
const std::vector<unsigned int> &GetIndices() const {
return indices;
}
@@ -998,7 +998,7 @@ public:
LazyObject *LazyDestinationObject() const;
/** return the name of the property the connection is attached to.
- * this is an empty string for object to object (OO) connections. */
+ * this is an empty string for object to object (OO) connections. */
const std::string &PropertyName() const {
return prop;
}
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h
index ba86191c4a..0489ce10ce 100644
--- a/modules/fbx/fbx_parser/FBXDocumentUtil.h
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h
@@ -107,12 +107,12 @@ const T *ProcessSimpleConnection(const Connection &con,
const char **propNameOut = nullptr) {
if (is_object_property_conn && !con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
- " link to be an object-object connection, ignoring",
+ " link to be an object-object connection, ignoring",
element);
return nullptr;
} else if (!is_object_property_conn && con.PropertyName().length()) {
DOMWarning("expected incoming " + std::string(name) +
- " link to be an object-property connection, ignoring",
+ " link to be an object-property connection, ignoring",
element);
return nullptr;
}
diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h
index b016db174b..bc22386957 100644
--- a/modules/fbx/fbx_parser/FBXImportSettings.h
+++ b/modules/fbx/fbx_parser/FBXImportSettings.h
@@ -81,29 +81,29 @@ namespace FBXDocParser {
/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
struct ImportSettings {
/** enable strict mode:
- * - only accept fbx 2012, 2013 files
- * - on the slightest error, give up.
- *
- * Basically, strict mode means that the fbx file will actually
- * be validated.*/
+ * - only accept fbx 2012, 2013 files
+ * - on the slightest error, give up.
+ *
+ * Basically, strict mode means that the fbx file will actually
+ * be validated.*/
bool strictMode = true;
/** specifies whether all geometry layers are read and scanned for
- * usable data channels. The FBX spec indicates that many readers
- * will only read the first channel and that this is in some way
- * the recommended way- in reality, however, it happens a lot that
- * vertex data is spread among multiple layers.*/
+ * usable data channels. The FBX spec indicates that many readers
+ * will only read the first channel and that this is in some way
+ * the recommended way- in reality, however, it happens a lot that
+ * vertex data is spread among multiple layers.*/
bool readAllLayers = true;
/** specifies whether all materials are read, or only those that
- * are referenced by at least one mesh. Reading all materials
- * may make FBX reading a lot slower since all objects
- * need to be processed.
- * This bit is ignored unless readMaterials=true.*/
+ * are referenced by at least one mesh. Reading all materials
+ * may make FBX reading a lot slower since all objects
+ * need to be processed.
+ * This bit is ignored unless readMaterials=true.*/
bool readAllMaterials = true;
/** import materials (true) or skip them and assign a default
- * material.*/
+ * material.*/
bool readMaterials = true;
/** import embedded textures?*/
@@ -116,35 +116,35 @@ struct ImportSettings {
bool readLights = true;
/** import animations (i.e. animation curves, the node
- * skeleton is always imported).*/
+ * skeleton is always imported).*/
bool readAnimations = true;
/** read bones (vertex weights and deform info).*/
bool readWeights = true;
/** preserve transformation pivots and offsets. Since these can
- * not directly be represented in assimp, additional dummy
- * nodes will be generated. Note that settings this to false
- * can make animation import a lot slower.
- *
- * The naming scheme for the generated nodes is:
- * <OriginalName>_$AssimpFbx$_<TransformName>
- *
- * where <TransformName> is one of
- * RotationPivot
- * RotationOffset
- * PreRotation
- * PostRotation
- * ScalingPivot
- * ScalingOffset
- * Translation
- * Scaling
- * Rotation
- **/
+ * not directly be represented in assimp, additional dummy
+ * nodes will be generated. Note that settings this to false
+ * can make animation import a lot slower.
+ *
+ * The naming scheme for the generated nodes is:
+ * <OriginalName>_$AssimpFbx$_<TransformName>
+ *
+ * where <TransformName> is one of
+ * RotationPivot
+ * RotationOffset
+ * PreRotation
+ * PostRotation
+ * ScalingPivot
+ * ScalingOffset
+ * Translation
+ * Scaling
+ * Rotation
+ **/
bool preservePivots = true;
/** do not import animation curves that specify a constant
- * values matching the corresponding node transformation.*/
+ * values matching the corresponding node transformation.*/
bool optimizeEmptyAnimationCurves = true;
/** use legacy naming for embedded textures eg: (*0, *1, *2).*/
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
index 2cc25a0690..2bb634ea56 100644
--- a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
@@ -368,7 +368,7 @@ MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array(
// UVIndex, MaterialIndex, NormalIndex, etc..
std::string indexDataElementName;
- if (indexOverride != "") {
+ if (!indexOverride.empty()) {
// Colors should become ColorIndex
indexDataElementName = indexOverride;
} else {
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h
index c9b25f008d..26fc1914d1 100644
--- a/modules/fbx/fbx_parser/FBXMeshGeometry.h
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.h
@@ -226,7 +226,7 @@ public:
const std::vector<Vector3> &GetVertices() const;
/** Get a list of all vertex normals or an empty array if
- * no normals are specified. */
+ * no normals are specified. */
const std::vector<Vector3> &GetNormals() const;
/** Return list of vertex indices. */
@@ -238,8 +238,8 @@ private:
std::vector<unsigned int> m_indices;
};
/**
-* DOM class for FBX geometry of type "Line"
-*/
+ * DOM class for FBX geometry of type "Line"
+ */
class LineGeometry : public Geometry {
public:
/** The class constructor */
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
index d9ef025a16..dbc9a0e46d 100644
--- a/modules/fbx/fbx_parser/FBXParser.cpp
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -660,13 +660,6 @@ void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) {
static_cast<real_t>(d[1]),
static_cast<real_t>(d[2])));
}
- // for debugging
- /*for ( size_t i = 0; i < out.size(); i++ ) {
- aiVector3D vec3( out[ i ] );
- std::stringstream stream;
- stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl;
- DefaultLogger::get()->info( stream.str() );
- }*/
} else if (type == 'f') {
const float *f = reinterpret_cast<const float *>(&buff[0]);
for (unsigned int i = 0; i < count3; ++i, f += 3) {
diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h
index 93836c2205..27db18bf8a 100644
--- a/modules/fbx/fbx_parser/FBXParser.h
+++ b/modules/fbx/fbx_parser/FBXParser.h
@@ -187,7 +187,7 @@ private:
class Parser {
public:
/** Parse given a token list. Does not take ownership of the tokens -
- * the objects must persist during the entire parser lifetime */
+ * the objects must persist during the entire parser lifetime */
Parser(const TokenList &tokens, bool is_binary);
~Parser();
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
index 37717e9109..b8c0f685ac 100644
--- a/modules/fbx/fbx_parser/FBXProperties.cpp
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -114,12 +114,12 @@ PropertyPtr ReadTypedProperty(const ElementPtr element) {
} else if (!strcmp(cs, "KTime")) {
return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4]));
} else if (!strcmp(cs, "Vector3D") ||
- !strcmp(cs, "ColorRGB") ||
- !strcmp(cs, "Vector") ||
- !strcmp(cs, "Color") ||
- !strcmp(cs, "Lcl Translation") ||
- !strcmp(cs, "Lcl Rotation") ||
- !strcmp(cs, "Lcl Scaling")) {
+ !strcmp(cs, "ColorRGB") ||
+ !strcmp(cs, "Vector") ||
+ !strcmp(cs, "Color") ||
+ !strcmp(cs, "Lcl Translation") ||
+ !strcmp(cs, "Lcl Rotation") ||
+ !strcmp(cs, "Lcl Scaling")) {
return new TypedProperty<Vector3>(Vector3(
ParseTokenAsFloat(tok[4]),
ParseTokenAsFloat(tok[5]),
diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp
index 1f14a69099..df46bd85c7 100644
--- a/modules/fbx/fbx_parser/FBXUtil.cpp
+++ b/modules/fbx/fbx_parser/FBXUtil.cpp
@@ -169,10 +169,10 @@ char EncodeBase64(char byte) {
}
/** Encodes a block of 4 bytes to base64 encoding
-* @param bytes Bytes to encode.
-* @param out_string String to write encoded values to.
-* @param string_pos Position in out_string.
-*/
+ * @param bytes Bytes to encode.
+ * @param out_string String to write encoded values to.
+ * @param string_pos Position in out_string.
+ */
void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) {
char b0 = (bytes[0] & 0xFC) >> 2;
char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h
index efc131831b..dab2a4201e 100644
--- a/modules/fbx/fbx_parser/FBXUtil.h
+++ b/modules/fbx/fbx_parser/FBXUtil.h
@@ -87,34 +87,34 @@ namespace Util {
const char *TokenTypeString(TokenType t);
/** Decode a single Base64-encoded character.
-*
-* @param ch Character to decode (from base64 to binary).
-* @return decoded byte value*/
+ *
+ * @param ch Character to decode (from base64 to binary).
+ * @return decoded byte value*/
uint8_t DecodeBase64(char ch);
/** Compute decoded size of a Base64-encoded string
-*
-* @param in Characters to decode.
-* @param inLength Number of characters to decode.
-* @return size of the decoded data (number of bytes)*/
+ *
+ * @param in Characters to decode.
+ * @param inLength Number of characters to decode.
+ * @return size of the decoded data (number of bytes)*/
size_t ComputeDecodedSizeBase64(const char *in, size_t inLength);
/** Decode a Base64-encoded string
-*
-* @param in Characters to decode.
-* @param inLength Number of characters to decode.
-* @param out Pointer where we will store the decoded data.
-* @param maxOutLength Size of output buffer.
-* @return size of the decoded data (number of bytes)*/
+ *
+ * @param in Characters to decode.
+ * @param inLength Number of characters to decode.
+ * @param out Pointer where we will store the decoded data.
+ * @param maxOutLength Size of output buffer.
+ * @return size of the decoded data (number of bytes)*/
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength);
char EncodeBase64(char byte);
/** Encode bytes in base64-encoding
-*
-* @param data Binary data to encode.
-* @param inLength Number of bytes to encode.
-* @return base64-encoded string*/
+ *
+ * @param data Binary data to encode.
+ * @param inLength Number of bytes to encode.
+ * @return base64-encoded string*/
std::string EncodeBase64(const char *data, size_t length);
} // namespace Util
} // namespace FBXDocParser
diff --git a/modules/fbx/register_types.cpp b/modules/fbx/register_types.cpp
index a75da8f3a9..d5e520a060 100644
--- a/modules/fbx/register_types.cpp
+++ b/modules/fbx/register_types.cpp
@@ -35,7 +35,7 @@
#ifdef TOOLS_ENABLED
static void _editor_init() {
- Ref<EditorSceneImporterFBX> import_fbx;
+ Ref<EditorSceneFormatImporterFBX> import_fbx;
import_fbx.instantiate();
ResourceImporterScene::get_singleton()->add_importer(import_fbx);
}
@@ -46,7 +46,7 @@ void register_fbx_types() {
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);
- GDREGISTER_CLASS(EditorSceneImporterFBX);
+ GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
ClassDB::set_current_api(prev_api);
diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp
index 66b0153308..bb95d120af 100644
--- a/modules/fbx/tools/import_utils.cpp
+++ b/modules/fbx/tools/import_utils.cpp
@@ -45,27 +45,27 @@ Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
- ret.set_euler_zyx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XYZ);
break;
case FBXDocParser::Model::RotOrder_EulerXZY:
- ret.set_euler_yzx(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
break;
case FBXDocParser::Model::RotOrder_EulerYZX:
- ret.set_euler_xzy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
break;
case FBXDocParser::Model::RotOrder_EulerYXZ:
- ret.set_euler_zxy(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
break;
case FBXDocParser::Model::RotOrder_EulerZXY:
- ret.set_euler_yxz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
break;
case FBXDocParser::Model::RotOrder_EulerZYX:
- ret.set_euler_xyz(p_rotation);
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
break;
case FBXDocParser::Model::RotOrder_SphericXYZ:
@@ -89,22 +89,22 @@ Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basi
// by simply invert its order: https://www.cs.utexas.edu/~theshark/courses/cs354/lectures/cs354-14.pdf
switch (mode) {
case FBXDocParser::Model::RotOrder_EulerXYZ:
- return p_rotation.get_euler_zyx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XYZ);
case FBXDocParser::Model::RotOrder_EulerXZY:
- return p_rotation.get_euler_yzx();
+ return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
case FBXDocParser::Model::RotOrder_EulerYZX:
- return p_rotation.get_euler_xzy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
case FBXDocParser::Model::RotOrder_EulerYXZ:
- return p_rotation.get_euler_zxy();
+ return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
case FBXDocParser::Model::RotOrder_EulerZXY:
- return p_rotation.get_euler_yxz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
case FBXDocParser::Model::RotOrder_EulerZYX:
- return p_rotation.get_euler_xyz();
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
case FBXDocParser::Model::RotOrder_SphericXYZ:
// TODO
diff --git a/modules/fbx/tools/import_utils.h b/modules/fbx/tools/import_utils.h
index fbe7dbd82f..88c71fb87e 100644
--- a/modules/fbx/tools/import_utils.h
+++ b/modules/fbx/tools/import_utils.h
@@ -43,7 +43,7 @@
/**
* Import Utils
* Conversion tools / glue code to convert from FBX to Godot
-*/
+ */
class ImportUtils {
public:
/// Convert a vector from degrees to radians.
@@ -201,7 +201,7 @@ public:
};
/** Get fbx fps for time mode meta data
- */
+ */
static float get_fbx_fps(int32_t time_mode) {
switch (time_mode) {
case AssetImportFbx::TIME_MODE_DEFAULT:
@@ -258,13 +258,13 @@ public:
}
/**
- * Find hardcoded textures from assimp which could be in many different directories
- */
+ * Find hardcoded textures from assimp which could be in many different directories
+ */
/**
- * set_texture_mapping_mode
- * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
- */
+ * set_texture_mapping_mode
+ * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
+ */
// static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
// ERR_FAIL_COND(texture.is_null());
// ERR_FAIL_COND(map_mode == nullptr);
@@ -282,9 +282,9 @@ public:
// }
/**
- * Load or load from cache image :)
- * We need to upgrade this in the later version :) should not be hard
- */
+ * Load or load from cache image :)
+ * We need to upgrade this in the later version :) should not be hard
+ */
//static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path){
// Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
diff --git a/modules/fbx/tools/validation_tools.h b/modules/fbx/tools/validation_tools.h
index 906a721045..12d644ee94 100644
--- a/modules/fbx/tools/validation_tools.h
+++ b/modules/fbx/tools/validation_tools.h
@@ -53,9 +53,9 @@ protected:
String csv_header = "file_path, error message, extra data\n";
massive_log_file += csv_header;
- for (Map<String, LocalVector<String>>::Element *element = validation_entries.front(); element; element = element->next()) {
- for (unsigned int x = 0; x < element->value().size(); x++) {
- const String &line_entry = element->key() + ", " + element->value()[x].c_escape() + "\n";
+ for (const KeyValue<String, LocalVector<String>> &element : validation_entries) {
+ for (unsigned int x = 0; x < element.value.size(); x++) {
+ const String &line_entry = element.key + ", " + element.value[x].c_escape() + "\n";
massive_log_file += line_entry;
}
}
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 45354ce692..f7f21a433e 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -16,11 +16,8 @@ env_gdnative.Prepend(CPPPATH=["#modules/gdnative/include/"])
Export("env_gdnative")
-SConscript("net/SCsub")
-SConscript("xr/SCsub")
SConscript("pluginscript/SCsub")
SConscript("videodecoder/SCsub")
-SConscript("text/SCsub")
import gdnative_builders
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index fa985501b5..026a84a70f 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -10,14 +10,9 @@ def get_doc_classes():
return [
"GDNative",
"GDNativeLibrary",
- "MultiplayerPeerGDNative",
"NativeScript",
- "PacketPeerGDNative",
"PluginScript",
- "StreamPeerGDNative",
"VideoStreamGDNative",
- "WebRTCPeerConnectionGDNative",
- "WebRTCDataChannelGDNative",
]
diff --git a/modules/gdnative/doc_classes/GDNative.xml b/modules/gdnative/doc_classes/GDNative.xml
index e4c5d34a2c..4bc149b119 100644
--- a/modules/gdnative/doc_classes/GDNative.xml
+++ b/modules/gdnative/doc_classes/GDNative.xml
@@ -30,6 +30,4 @@
<member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 94eae3cd06..21df640ebc 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -7,8 +7,8 @@
A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as XRInterfaceGDNative. The library must be compiled for each platform and architecture that the project will run on.
</description>
<tutorials>
- <link title="GDNative C example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html</link>
- <link title="GDNative C++ example">https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-cpp-example.html</link>
+ <link title="GDNative C example">$DOCS_URL/tutorials/scripting/gdnative/gdnative_c_example.html</link>
+ <link title="GDNative C++ example">$DOCS_URL/tutorials/scripting/gdnative/gdnative_cpp_example.html</link>
</tutorials>
<methods>
<method name="get_current_dependencies" qualifiers="const">
@@ -45,6 +45,4 @@
On platforms that require statically linking libraries (currently only iOS), each library must have a different [code]symbol_prefix[/code].
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml
index 397d12a3a9..221374a7a4 100644
--- a/modules/gdnative/doc_classes/NativeScript.xml
+++ b/modules/gdnative/doc_classes/NativeScript.xml
@@ -38,7 +38,7 @@
<return type="Variant" />
<description>
Constructs a new object of the base type with a script of this type already attached.
- [i]Note[/i]: Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension.
+ [b]Note:[/b] Any arguments passed to this function will be ignored and not passed to the native constructor function. This will change with in a future API extension.
</description>
</method>
</methods>
@@ -52,6 +52,4 @@
<member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name" default="&quot;&quot;">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/PluginScript.xml b/modules/gdnative/doc_classes/PluginScript.xml
index 8e28187482..ec80ade394 100644
--- a/modules/gdnative/doc_classes/PluginScript.xml
+++ b/modules/gdnative/doc_classes/PluginScript.xml
@@ -14,6 +14,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml
index 8b1a3210df..dc64e8fc18 100644
--- a/modules/gdnative/doc_classes/VideoStreamGDNative.xml
+++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml
@@ -24,6 +24,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml b/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
deleted file mode 100644
index f32a4f0a23..0000000000
--- a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml b/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
deleted file mode 100644
index 82f8633bb6..0000000000
--- a/modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeerConnectionGDNative" inherits="WebRTCPeerConnection" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp
index b84ce2d192..e0de1a0505 100644
--- a/modules/gdnative/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative/gdnative.cpp
@@ -129,13 +129,13 @@ void GDAPI godot_free(void *p_ptr) {
// Helper print functions.
void GDAPI godot_print_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_ERROR);
+ _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_ERROR);
}
void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_WARNING);
+ _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_WARNING);
}
void GDAPI godot_print_script_error(const char *p_description, const char *p_function, const char *p_file, int p_line) {
- _err_print_error(p_function, p_file, p_line, p_description, ERR_HANDLER_SCRIPT);
+ _err_print_error(p_function, p_file, p_line, p_description, false, ERR_HANDLER_SCRIPT);
}
void _gdnative_report_version_mismatch(const godot_object *p_library, const char *p_ext, godot_gdnative_api_version p_want, godot_gdnative_api_version p_have) {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 66d2dc267d..cf1c7dc01f 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5103,580 +5103,6 @@
]
}
]
- },
- {
- "name": "net",
- "type": "NET",
- "version": {
- "major": 4,
- "minor": 0
- },
- "next": null,
- "api": [
- {
- "name": "godot_net_bind_stream_peer",
- "return_type": "void",
- "arguments": [
- [
- "godot_object *",
- "p_obj"
- ],
- [
- "const godot_net_stream_peer *",
- "p_interface"
- ]
- ]
- },
- {
- "name": "godot_net_bind_packet_peer",
- "return_type": "void",
- "arguments": [
- [
- "godot_object *",
- "p_obj"
- ],
- [
- "const godot_net_packet_peer *",
- "p_interface"
- ]
- ]
- },
- {
- "name": "godot_net_bind_multiplayer_peer",
- "return_type": "void",
- "arguments": [
- [
- "godot_object *",
- "p_obj"
- ],
- [
- "const godot_net_multiplayer_peer *",
- "p_interface"
- ]
- ]
- },
- {
- "name": "godot_net_set_webrtc_library",
- "return_type": "godot_error",
- "arguments": [
- [
- "const godot_net_webrtc_library *",
- "p_library"
- ]
- ]
- },
- {
- "name": "godot_net_bind_webrtc_peer_connection",
- "return_type": "void",
- "arguments": [
- [
- "godot_object *",
- "p_obj"
- ],
- [
- "const godot_net_webrtc_peer_connection *",
- "p_interface"
- ]
- ]
- },
- {
- "name": "godot_net_bind_webrtc_data_channel",
- "return_type": "void",
- "arguments": [
- [
- "godot_object *",
- "p_obj"
- ],
- [
- "const godot_net_webrtc_data_channel *",
- "p_interface"
- ]
- ]
- }
- ]
- },
- {
- "name": "text",
- "type": "TEXT",
- "version": {
- "major": 1,
- "minor": 0
- },
- "next": null,
- "api": [
- {
- "name": "godot_text_register_interface",
- "return_type": "void",
- "arguments": [
- [
- "const godot_text_interface_gdnative *",
- "p_interface"
- ],
- [
- "const godot_string *",
- "p_name"
- ],
- [
- "uint32_t",
- "p_features"
- ]
- ]
- },
- {
- "name": "godot_glyph_new",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "r_dest"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_range",
- "return_type": "godot_vector2i",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_range",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "const godot_vector2i *",
- "p_range"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_count",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_count",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_int",
- "p_count"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_repeat",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_repeat",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_int",
- "p_repeat"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_flags",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_flags",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_int",
- "p_flags"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_offset",
- "return_type": "godot_vector2",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_offset",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "const godot_vector2 *",
- "p_offset"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_advance",
- "return_type": "godot_real_t",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_advance",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_real_t",
- "p_advance"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_font",
- "return_type": "godot_rid",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_font",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_rid *",
- "p_font"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_font_size",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_font_size",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_int",
- "p_size"
- ]
- ]
- },
- {
- "name": "godot_glyph_get_index",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_glyph *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_glyph_set_index",
- "return_type": "void",
- "arguments": [
- [
- "godot_glyph *",
- "p_self"
- ],
- [
- "godot_int",
- "p_index"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_new",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "r_dest"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_new_copy",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "r_dest"
- ],
- [
- "const godot_packed_glyph_array *",
- "p_src"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_is_empty",
- "return_type": "godot_bool",
- "arguments": [
- [
- "const godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_append",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_glyph *",
- "p_data"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_append_array",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_packed_glyph_array *",
- "p_array"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_insert",
- "return_type": "godot_error",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_int",
- "p_idx"
- ],
- [
- "const godot_glyph *",
- "p_data"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_has",
- "return_type": "godot_bool",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_glyph *",
- "p_value"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_sort",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_reverse",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_push_back",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_glyph *",
- "p_data"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_remove",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_int",
- "p_idx"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_resize",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_int",
- "p_size"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_ptr",
- "return_type": "const godot_glyph *",
- "arguments": [
- [
- "const godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_ptrw",
- "return_type": "godot_glyph *",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_set",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_int",
- "p_idx"
- ],
- [
- "const godot_glyph *",
- "p_data"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_get",
- "return_type": "godot_glyph",
- "arguments": [
- [
- "const godot_packed_glyph_array *",
- "p_self"
- ],
- [
- "const godot_int",
- "p_idx"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_size",
- "return_type": "godot_int",
- "arguments": [
- [
- "const godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- },
- {
- "name": "godot_packed_glyph_array_destroy",
- "return_type": "void",
- "arguments": [
- [
- "godot_packed_glyph_array *",
- "p_self"
- ]
- ]
- }
- ]
}
]
}
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
index 181fd71b82..6c96e23426 100644
--- a/modules/gdnative/gdnative_builders.py
+++ b/modules/gdnative/gdnative_builders.py
@@ -20,10 +20,8 @@ def _build_gdnative_api_struct_header(api):
"#include <gdnative/gdnative.h>",
"#include <android/godot_android.h>",
"#include <nativescript/godot_nativescript.h>",
- "#include <net/godot_net.h>",
"#include <pluginscript/godot_pluginscript.h>",
"#include <videodecoder/godot_videodecoder.h>",
- "#include <text/godot_text.h>",
"",
"#ifdef __cplusplus",
'extern "C" {',
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index f965bcd014..df3c37f730 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -38,9 +38,9 @@ void GDNativeLibraryEditor::edit(Ref<GDNativeLibrary> p_library) {
library = p_library;
Ref<ConfigFile> config = p_library->get_config_file();
- for (Map<String, NativePlatformConfig>::Element *E = platforms.front(); E; E = E->next()) {
- for (List<String>::Element *it = E->value().entries.front(); it; it = it->next()) {
- String target = E->key() + "." + it->get();
+ for (KeyValue<String, NativePlatformConfig> &E : platforms) {
+ for (List<String>::Element *it = E.value.entries.front(); it; it = it->next()) {
+ String target = E.key + "." + it->get();
TargetConfig ecfg;
ecfg.library = config->get_value("entry", target, "");
ecfg.dependencies = config->get_value("dependencies", target, Array());
@@ -110,7 +110,7 @@ void GDNativeLibraryEditor::_update_tree() {
TreeItem *new_arch = tree->create_item(platform);
new_arch->set_text(0, TTR("Double click to create a new entry"));
- new_arch->set_text_align(0, TreeItem::ALIGN_CENTER);
+ new_arch->set_text_alignment(0, HORIZONTAL_ALIGNMENT_CENTER);
new_arch->set_custom_color(0, get_theme_color(SNAME("accent_color"), SNAME("Editor")));
new_arch->set_expand_right(0, true);
new_arch->set_metadata(1, E->key());
@@ -245,9 +245,9 @@ void GDNativeLibraryEditor::_translate_to_config_file() {
config->erase_section("entry");
config->erase_section("dependencies");
- for (Map<String, NativePlatformConfig>::Element *E = platforms.front(); E; E = E->next()) {
- for (List<String>::Element *it = E->value().entries.front(); it; it = it->next()) {
- String target = E->key() + "." + it->get();
+ for (KeyValue<String, NativePlatformConfig> &E : platforms) {
+ for (List<String>::Element *it = E.value.entries.front(); it; it = it->next()) {
+ String target = E.key + "." + it->get();
if (entry_configs[target].library.is_empty() && entry_configs[target].dependencies.is_empty()) {
continue;
}
@@ -335,15 +335,15 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
hbox->add_child(label);
filter = memnew(MenuButton);
filter->set_h_size_flags(SIZE_EXPAND_FILL);
- filter->set_text_align(filter->ALIGN_LEFT);
+ filter->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
hbox->add_child(filter);
PopupMenu *filter_list = filter->get_popup();
filter_list->set_hide_on_checkable_item_selection(false);
int idx = 0;
- for (Map<String, NativePlatformConfig>::Element *E = platforms.front(); E; E = E->next()) {
- filter_list->add_check_item(E->get().name, idx);
- filter_list->set_item_metadata(idx, E->key());
+ for (const KeyValue<String, NativePlatformConfig> &E : platforms) {
+ filter_list->add_check_item(E.value.name, idx);
+ filter_list->set_item_metadata(idx, E.key);
filter_list->set_item_checked(idx, true);
idx += 1;
}
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 09eac2492f..bc53a4001d 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -39,7 +39,7 @@ extern "C" {
typedef enum {
GODOT_METHOD_RPC_MODE_DISABLED,
- GODOT_METHOD_RPC_MODE_ANY,
+ GODOT_METHOD_RPC_MODE_ANY_PEER,
GODOT_METHOD_RPC_MODE_AUTHORITY,
} godot_nativescript_method_rpc_mode;
@@ -102,7 +102,7 @@ typedef enum {
GODOT_PROPERTY_USAGE_DEFAULT = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK,
GODOT_PROPERTY_USAGE_DEFAULT_INTL = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_EDITOR | GODOT_PROPERTY_USAGE_NETWORK | GODOT_PROPERTY_USAGE_INTERNATIONALIZED,
- GODOT_PROPERTY_USAGE_NOEDITOR = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_NETWORK,
+ GODOT_PROPERTY_USAGE_NO_EDITOR = GODOT_PROPERTY_USAGE_STORAGE | GODOT_PROPERTY_USAGE_NETWORK,
} godot_nativescript_property_usage_flags;
typedef struct {
diff --git a/modules/gdnative/include/net/godot_net.h b/modules/gdnative/include/net/godot_net.h
deleted file mode 100644
index 3fb7b9e1cc..0000000000
--- a/modules/gdnative/include/net/godot_net.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*************************************************************************/
-/* godot_net.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef GODOT_NATIVENET_H
-#define GODOT_NATIVENET_H
-
-#include <gdnative/gdnative.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// For future versions of the API we should only add new functions at the end of the structure and use the
-// version info to detect whether a call is available
-
-// Use these to populate version in your plugin
-#define GODOT_NET_API_MAJOR 3
-#define GODOT_NET_API_MINOR 1
-
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
- godot_object *data; /* User reference */
-
- /* This is StreamPeer */
- godot_error (*get_data)(void *user, uint8_t *p_buffer, int p_bytes);
- godot_error (*get_partial_data)(void *user, uint8_t *p_buffer, int p_bytes, int *r_received);
- godot_error (*put_data)(void *user, const uint8_t *p_data, int p_bytes);
- godot_error (*put_partial_data)(void *user, const uint8_t *p_data, int p_bytes, int *r_sent);
-
- int (*get_available_bytes)(const void *user);
-
- void *next; /* For extension? */
-} godot_net_stream_peer;
-
-/* Binds a StreamPeerGDNative to the provided interface */
-void godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface);
-
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is PacketPeer */
- godot_error (*get_packet)(void *, const uint8_t **, int *);
- godot_error (*put_packet)(void *, const uint8_t *, int);
- godot_int (*get_available_packet_count)(const void *);
- godot_int (*get_max_packet_size)(const void *);
-
- void *next; /* For extension? */
-} godot_net_packet_peer;
-
-/* Binds a PacketPeerGDNative to the provided interface */
-void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *);
-
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is PacketPeer */
- godot_error (*get_packet)(void *, const uint8_t **, int *);
- godot_error (*put_packet)(void *, const uint8_t *, int);
- godot_int (*get_available_packet_count)(const void *);
- godot_int (*get_max_packet_size)(const void *);
-
- /* This is MultiplayerPeer */
- void (*set_transfer_channel)(void *, godot_int);
- godot_int (*get_transfer_channel)(void *);
- void (*set_transfer_mode)(void *, godot_int);
- godot_int (*get_transfer_mode)(const void *);
- // 0 = broadcast, 1 = server, <0 = all but abs(value)
- void (*set_target_peer)(void *, godot_int);
- godot_int (*get_packet_peer)(const void *);
- godot_bool (*is_server)(const void *);
- void (*poll)(void *);
- // Must be > 0, 1 is for server
- int32_t (*get_unique_id)(const void *);
- void (*set_refuse_new_connections)(void *, godot_bool);
- godot_bool (*is_refusing_new_connections)(const void *);
- godot_int (*get_connection_status)(const void *);
-
- void *next; /* For extension? Or maybe not... */
-} godot_net_multiplayer_peer;
-
-/* Binds a MultiplayerPeerGDNative to the provided interface */
-void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *);
-
-#ifdef __cplusplus
-}
-#endif
-
-// WebRTC Bindings
-#include "net/godot_webrtc.h"
-
-#endif /* GODOT_NATIVENET_H */
diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h
deleted file mode 100644
index 52006e56ec..0000000000
--- a/modules/gdnative/include/net/godot_webrtc.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*************************************************************************/
-/* godot_webrtc.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef GODOT_NATIVEWEBRTC_H
-#define GODOT_NATIVEWEBRTC_H
-
-#include <gdnative/gdnative.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GODOT_NET_WEBRTC_API_MAJOR 4
-#define GODOT_NET_WEBRTC_API_MINOR 0
-
-/* Library Interface (used to set default GDNative WebRTC implementation */
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- /* Called when the library is unset as default interface via godot_net_set_webrtc_library */
- void (*unregistered)();
-
- /* Used by WebRTCPeerConnection create when GDNative is the default implementation. */
- /* Takes a pointer to WebRTCPeerConnectionGDNative, should bind and return OK, failure if binding was unsuccessful. */
- godot_error (*create_peer_connection)(godot_object *);
-
- void *next; /* For extension */
-} godot_net_webrtc_library;
-
-/* WebRTCPeerConnection interface */
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is WebRTCPeerConnection */
- godot_int (*get_connection_state)(const void *);
-
- godot_error (*initialize)(void *, const godot_dictionary *);
- godot_object *(*create_data_channel)(void *, const char *p_channel_name, const godot_dictionary *);
- godot_error (*create_offer)(void *);
- godot_error (*create_answer)(void *); /* unused for now, should be done automatically on set_local_description */
- godot_error (*set_remote_description)(void *, const char *, const char *);
- godot_error (*set_local_description)(void *, const char *, const char *);
- godot_error (*add_ice_candidate)(void *, const char *, int, const char *);
- godot_error (*poll)(void *);
- void (*close)(void *);
-
- void *next; /* For extension? */
-} godot_net_webrtc_peer_connection;
-
-/* WebRTCDataChannel interface */
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
-
- godot_object *data; /* User reference */
-
- /* This is PacketPeer */
- godot_error (*get_packet)(void *, const uint8_t **, int *);
- godot_error (*put_packet)(void *, const uint8_t *, int);
- godot_int (*get_available_packet_count)(const void *);
- godot_int (*get_max_packet_size)(const void *);
-
- /* This is WebRTCDataChannel */
- void (*set_write_mode)(void *, godot_int);
- godot_int (*get_write_mode)(const void *);
- bool (*was_string_packet)(const void *);
-
- godot_int (*get_ready_state)(const void *);
- const char *(*get_label)(const void *);
- bool (*is_ordered)(const void *);
- int (*get_id)(const void *);
- int (*get_max_packet_life_time)(const void *);
- int (*get_max_retransmits)(const void *);
- const char *(*get_protocol)(const void *);
- bool (*is_negotiated)(const void *);
- int (*get_buffered_amount)(const void *);
-
- godot_error (*poll)(void *);
- void (*close)(void *);
-
- void *next; /* For extension? */
-} godot_net_webrtc_data_channel;
-
-/* Set the default GDNative library */
-godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *);
-/* Binds a WebRTCPeerConnectionGDNative to the provided interface */
-void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *);
-/* Binds a WebRTCDataChannelGDNative to the provided interface */
-void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/modules/gdnative/include/text/godot_text.h b/modules/gdnative/include/text/godot_text.h
deleted file mode 100644
index 940cfd11f8..0000000000
--- a/modules/gdnative/include/text/godot_text.h
+++ /dev/null
@@ -1,283 +0,0 @@
-/*************************************************************************/
-/* godot_text.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef GODOT_NATIVETEXT_H
-#define GODOT_NATIVETEXT_H
-
-#include <gdnative/gdnative.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GODOT_TEXT_API_MAJOR 1
-#define GODOT_TEXT_API_MINOR 0
-
-#define GODOT_GLYPH_SIZE 40
-
-#ifndef GODOT_TEXT_API_GODOT_GLYPH_TYPE_DEFINED
-#define GODOT_TEXT_API_GODOT_GLYPH_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_GLYPH_SIZE];
-} godot_glyph;
-#endif
-
-#define GODOT_PACKED_GLYPH_ARRAY_SIZE (2 * sizeof(void *))
-
-#ifndef GODOT_TEXT_API_GODOT_PACKED_GLYPH_ARRAY_TYPE_DEFINED
-#define GODOT_TEXT_API_GODOT_PACKED_GLYPH_ARRAY_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_PACKED_GLYPH_ARRAY_SIZE];
-} godot_packed_glyph_array;
-#endif
-
-typedef struct {
- godot_gdnative_api_version version;
-
- void *(*constructor)(godot_object *);
- void (*destructor)(void *);
-
- godot_string (*get_name)(const void *);
- godot_bool (*has_feature)(const void *, godot_int);
-
- void (*free)(void *, godot_rid *);
- bool (*has)(void *, godot_rid *);
-
- bool (*load_support_data)(void *, const godot_string *);
- godot_string (*get_support_data_filename)(const void *);
- godot_string (*get_support_data_info)(const void *);
- bool (*save_support_data)(void *, const godot_string *);
-
- bool (*is_locale_right_to_left)(void *, const godot_string *);
- int32_t (*name_to_tag)(const void *, const godot_string *);
- godot_string (*tag_to_name)(const void *, int32_t);
-
- godot_rid (*create_font)(void *);
- void (*font_set_data)(void *, godot_rid *, const godot_packed_byte_array *);
- void (*font_set_data_ptr)(void *, godot_rid *, const uint8_t *, size_t);
- void (*font_set_antialiased)(void *, godot_rid *, bool);
- bool (*font_is_antialiased)(const void *, godot_rid *);
- void (*font_set_multichannel_signed_distance_field)(void *, godot_rid *, bool);
- bool (*font_is_multichannel_signed_distance_field)(const void *, godot_rid *);
- void (*font_set_msdf_pixel_range)(void *, godot_rid *, godot_int);
- godot_int (*font_get_msdf_pixel_range)(const void *, godot_rid *);
- void (*font_set_msdf_size)(void *, godot_rid *, godot_int);
- godot_int (*font_get_msdf_size)(const void *, godot_rid *);
- void (*font_set_fixed_size)(void *, godot_rid *, godot_int);
- godot_int (*font_get_fixed_size)(const void *, godot_rid *);
- void (*font_set_force_autohinter)(void *, godot_rid *, bool);
- bool (*font_is_force_autohinter)(const void *, godot_rid *);
- void (*font_set_hinting)(void *, godot_rid *, godot_int);
- godot_int (*font_get_hinting)(const void *, godot_rid *);
- void (*font_set_variation_coordinates)(void *, godot_rid *, const godot_dictionary *);
- godot_dictionary (*font_get_variation_coordinates)(const void *, godot_rid *);
- void (*font_set_oversampling)(void *, godot_rid *, godot_real_t);
- godot_real_t (*font_get_oversampling)(const void *, godot_rid *);
- godot_array (*font_get_size_cache_list)(const void *, godot_rid *);
- void (*font_clear_size_cache)(void *, godot_rid *);
- void (*font_remove_size_cache)(void *, godot_rid *, const godot_vector2i *);
- void (*font_set_ascent)(void *, godot_rid *, godot_int, godot_real_t);
- godot_real_t (*font_get_ascent)(const void *, godot_rid *, godot_int);
- void (*font_set_descent)(void *, godot_rid *, godot_int, godot_real_t);
- godot_real_t (*font_get_descent)(const void *, godot_rid *, godot_int);
- void (*font_set_underline_position)(void *, godot_rid *, godot_int, godot_real_t);
- godot_real_t (*font_get_underline_position)(const void *, godot_rid *, godot_int);
- void (*font_set_underline_thickness)(void *, godot_rid *, godot_int, godot_real_t);
- godot_real_t (*font_get_underline_thickness)(const void *, godot_rid *, godot_int);
- void (*font_set_scale)(void *, godot_rid *, godot_int, godot_real_t);
- godot_real_t (*font_get_scale)(const void *, godot_rid *, godot_int);
- void (*font_set_spacing)(void *, godot_rid *, godot_int, godot_int, godot_int);
- godot_int (*font_get_spacing)(const void *, godot_rid *, godot_int, godot_int);
- godot_int (*font_get_texture_count)(const void *, godot_rid *, const godot_vector2i *);
- void (*font_clear_textures)(void *, godot_rid *, const godot_vector2i *);
- void (*font_remove_texture)(void *, godot_rid *, const godot_vector2i *, godot_int);
- void (*font_set_texture_image)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_object *);
- godot_object *(*font_get_texture_image)(const void *, godot_rid *, const godot_vector2i *, godot_int);
- void (*font_set_texture_offsets)(void *, godot_rid *, const godot_vector2i *, godot_int, const godot_packed_int32_array *);
- godot_packed_int32_array (*font_get_texture_offsets)(const void *, godot_rid *, const godot_vector2i *, godot_int);
- godot_array (*font_get_glyph_list)(const void *, godot_rid *, const godot_vector2i *);
- void (*font_clear_glyphs)(void *, godot_rid *, const godot_vector2i *);
- void (*font_remove_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t);
- godot_vector2 (*font_get_glyph_advance)(const void *, godot_rid *, godot_int, int32_t);
- void (*font_set_glyph_advance)(void *, godot_rid *, godot_int, int32_t, const godot_vector2 *);
- godot_vector2 (*font_get_glyph_offset)(const void *, godot_rid *, const godot_vector2i *, int32_t);
- void (*font_set_glyph_offset)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *);
- godot_vector2 (*font_get_glyph_size)(const void *, godot_rid *, const godot_vector2i *, int32_t);
- void (*font_set_glyph_size)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_vector2 *);
- godot_rect2 (*font_get_glyph_uv_rect)(const void *, godot_rid *, const godot_vector2i *, int32_t);
- void (*font_set_glyph_uv_rect)(void *, godot_rid *, const godot_vector2i *, int32_t, const godot_rect2 *);
- godot_int (*font_get_glyph_texture_idx)(const void *, godot_rid *, const godot_vector2i *, int32_t);
- void (*font_set_glyph_texture_idx)(void *, godot_rid *, const godot_vector2i *, int32_t, godot_int);
- bool (*font_get_glyph_contours)(const void *, godot_rid *, godot_int, int32_t, godot_packed_vector3_array *, godot_packed_int32_array *, bool *);
- godot_array (*font_get_kerning_list)(const void *, godot_rid *, godot_int);
- void (*font_clear_kerning_map)(void *, godot_rid *, godot_int);
- void (*font_remove_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *);
- void (*font_set_kerning)(void *, godot_rid *, godot_int, const godot_vector2i *, const godot_vector2 *);
- godot_vector2 (*font_get_kerning)(const void *, godot_rid *, godot_int, const godot_vector2i *);
- int32_t (*font_get_glyph_index)(const void *, godot_rid *, godot_int, char32_t, char32_t);
- bool (*font_has_char)(const void *, godot_rid *, char32_t);
- godot_string (*font_get_supported_chars)(const void *, godot_rid *);
- void (*font_render_range)(void *, godot_rid *, const godot_vector2i *, char32_t, char32_t);
- void (*font_render_glyph)(void *, godot_rid *, const godot_vector2i *, int32_t);
- void (*font_draw_glyph)(const void *, godot_rid *, godot_rid *, godot_int, const godot_vector2 *, int32_t, const godot_color *);
- void (*font_draw_glyph_outline)(const void *, godot_rid *, godot_rid *, godot_int, godot_int, const godot_vector2 *, int32_t, const godot_color *);
- bool (*font_is_language_supported)(const void *, godot_rid *, const godot_string *);
- void (*font_set_language_support_override)(void *, godot_rid *, const godot_string *, bool);
- bool (*font_get_language_support_override)(const void *, godot_rid *, const godot_string *);
- void (*font_remove_language_support_override)(void *, godot_rid *, const godot_string *);
- godot_packed_string_array (*font_get_language_support_overrides)(const void *, godot_rid *);
- bool (*font_is_script_supported)(const void *, godot_rid *, const godot_string *);
- void (*font_set_script_support_override)(void *, godot_rid *, const godot_string *, bool);
- bool (*font_get_script_support_override)(const void *, godot_rid *, const godot_string *);
- void (*font_remove_script_support_override)(void *, godot_rid *, const godot_string *);
- godot_packed_string_array (*font_get_script_support_overrides)(const void *, godot_rid *);
- godot_dictionary (*font_supported_feature_list)(const void *, godot_rid *);
- godot_dictionary (*font_supported_variation_list)(const void *, godot_rid *);
- godot_real_t (*font_get_global_oversampling)(const void *);
- void (*font_set_global_oversampling)(void *, godot_real_t);
-
- godot_rid (*create_shaped_text)(void *, godot_int, godot_int);
- void (*shaped_text_clear)(void *, godot_rid *);
- void (*shaped_text_set_direction)(void *, godot_rid *, godot_int);
- godot_int (*shaped_text_get_direction)(void *, godot_rid *);
- void (*shaped_text_set_bidi_override)(void *, godot_rid *, const godot_packed_vector2i_array *);
- void (*shaped_text_set_orientation)(void *, godot_rid *, godot_int);
- godot_int (*shaped_text_get_orientation)(void *, godot_rid *);
- void (*shaped_text_set_preserve_invalid)(void *, godot_rid *, bool);
- bool (*shaped_text_get_preserve_invalid)(void *, godot_rid *);
- void (*shaped_text_set_preserve_control)(void *, godot_rid *, bool);
- bool (*shaped_text_get_preserve_control)(void *, godot_rid *);
- bool (*shaped_text_add_string)(void *, godot_rid *, const godot_string *, const godot_rid **, int, const godot_dictionary *, const godot_string *);
- bool (*shaped_text_add_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int, godot_int);
- bool (*shaped_text_resize_object)(void *, godot_rid *, const godot_variant *, const godot_vector2 *, godot_int);
- godot_rid (*shaped_text_substr)(void *, godot_rid *, godot_int, godot_int);
- godot_rid (*shaped_text_get_parent)(void *, godot_rid *);
- godot_real_t (*shaped_text_fit_to_width)(void *, godot_rid *, godot_real_t, uint8_t);
- godot_real_t (*shaped_text_tab_align)(void *, godot_rid *, godot_packed_float32_array *);
- bool (*shaped_text_shape)(void *, godot_rid *);
- bool (*shaped_text_update_breaks)(void *, godot_rid *);
- bool (*shaped_text_update_justification_ops)(void *, godot_rid *);
- void (*shaped_text_overrun_trim_to_width)(void *, godot_rid *, godot_real_t, uint8_t);
- bool (*shaped_text_is_ready)(void *, godot_rid *);
- godot_packed_glyph_array (*shaped_text_get_glyphs)(void *, godot_rid *);
- godot_vector2i (*shaped_text_get_range)(void *, godot_rid *);
- godot_packed_glyph_array (*shaped_text_sort_logical)(void *, godot_rid *);
- godot_packed_vector2i_array (*shaped_text_get_line_breaks_adv)(void *, godot_rid *, godot_packed_float32_array *, int, bool, uint8_t);
- godot_packed_vector2i_array (*shaped_text_get_line_breaks)(void *, godot_rid *, godot_real_t, int, uint8_t);
- godot_packed_vector2i_array (*shaped_text_get_word_breaks)(void *, godot_rid *, int);
- godot_array (*shaped_text_get_objects)(void *, godot_rid *);
- godot_rect2 (*shaped_text_get_object_rect)(void *, godot_rid *, const godot_variant *);
- godot_vector2 (*shaped_text_get_size)(void *, godot_rid *);
- godot_real_t (*shaped_text_get_ascent)(void *, godot_rid *);
- godot_real_t (*shaped_text_get_descent)(void *, godot_rid *);
- godot_real_t (*shaped_text_get_width)(void *, godot_rid *);
- godot_real_t (*shaped_text_get_underline_position)(void *, godot_rid *);
- godot_real_t (*shaped_text_get_underline_thickness)(void *, godot_rid *);
-
- godot_string (*format_number)(void *, const godot_string *, const godot_string *);
- godot_string (*parse_number)(void *, const godot_string *, const godot_string *);
- godot_string (*percent_sign)(void *, const godot_string *);
-} godot_text_interface_gdnative;
-
-void GDAPI godot_text_register_interface(const godot_text_interface_gdnative *p_interface, const godot_string *p_name, uint32_t p_features);
-
-// Glyph
-
-void GDAPI godot_glyph_new(godot_glyph *r_dest);
-
-godot_vector2i GDAPI godot_glyph_get_range(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_range(godot_glyph *p_self, const godot_vector2i *p_range);
-
-godot_int GDAPI godot_glyph_get_count(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_count(godot_glyph *p_self, godot_int p_count);
-
-godot_int GDAPI godot_glyph_get_repeat(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_repeat(godot_glyph *p_self, godot_int p_repeat);
-
-godot_int GDAPI godot_glyph_get_flags(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags);
-
-godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset);
-
-godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance);
-
-godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font);
-
-godot_int GDAPI godot_glyph_get_font_size(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_font_size(godot_glyph *p_self, godot_int p_size);
-
-godot_int GDAPI godot_glyph_get_index(const godot_glyph *p_self);
-void GDAPI godot_glyph_set_index(godot_glyph *p_self, godot_int p_index);
-
-// GlyphArray
-
-void GDAPI godot_packed_glyph_array_new(godot_packed_glyph_array *r_dest);
-void GDAPI godot_packed_glyph_array_new_copy(godot_packed_glyph_array *r_dest, const godot_packed_glyph_array *p_src);
-
-const godot_glyph GDAPI *godot_packed_glyph_array_ptr(const godot_packed_glyph_array *p_self);
-godot_glyph GDAPI *godot_packed_glyph_array_ptrw(godot_packed_glyph_array *p_self);
-
-void GDAPI godot_packed_glyph_array_append(godot_packed_glyph_array *p_self, const godot_glyph *p_data);
-
-void GDAPI godot_packed_glyph_array_append_array(godot_packed_glyph_array *p_self, const godot_packed_glyph_array *p_array);
-
-godot_error GDAPI godot_packed_glyph_array_insert(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data);
-
-godot_bool GDAPI godot_packed_glyph_array_has(godot_packed_glyph_array *p_self, const godot_glyph *p_value);
-
-void GDAPI godot_packed_glyph_array_sort(godot_packed_glyph_array *p_self);
-
-void GDAPI godot_packed_glyph_array_reverse(godot_packed_glyph_array *p_self);
-
-void GDAPI godot_packed_glyph_array_push_back(godot_packed_glyph_array *p_self, const godot_glyph *p_data);
-
-void GDAPI godot_packed_glyph_array_remove(godot_packed_glyph_array *p_self, godot_int p_idx);
-
-void GDAPI godot_packed_glyph_array_resize(godot_packed_glyph_array *p_self, godot_int p_size);
-
-void GDAPI godot_packed_glyph_array_set(godot_packed_glyph_array *p_self, godot_int p_idx, const godot_glyph *p_data);
-godot_glyph GDAPI godot_packed_glyph_array_get(const godot_packed_glyph_array *p_self, godot_int p_idx);
-
-godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_self);
-
-godot_bool GDAPI godot_packed_glyph_array_is_empty(const godot_packed_glyph_array *p_self);
-
-void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self);
-
-// Grapheme
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !GODOT_NATIVETEXT_H */
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 2d9b45cb07..ae16c22849 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -223,8 +223,8 @@ List<ClassAPI> generate_c_api_classes() {
enum_api_map[enum_name] = enum_api;
}
}
- for (const Map<StringName, EnumAPI>::Element *E = enum_api_map.front(); E; E = E->next()) {
- global_constants_api.enums.push_back(E->get());
+ for (const KeyValue<StringName, EnumAPI> &E : enum_api_map) {
+ global_constants_api.enums.push_back(E.value);
}
global_constants_api.constants.sort_custom<ConstantAPIComparator>();
api.push_back(global_constants_api);
@@ -397,7 +397,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_type = "Variant";
} else if (arg_info.type == Variant::OBJECT) {
arg_type = arg_info.class_name;
- if (arg_type == "") {
+ if (arg_type.is_empty()) {
arg_type = Variant::get_type_name(arg_info.type);
}
} else {
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index 70b14836bf..dadd1a9d10 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -127,9 +127,9 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
E->get().methods.insert(p_function_name, method);
if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) {
- MultiplayerAPI::RPCConfig nd;
+ Multiplayer::RPCConfig nd;
nd.name = String(p_name);
- nd.rpc_mode = MultiplayerAPI::RPCMode(p_attr.rpc_type);
+ nd.rpc_mode = Multiplayer::RPCMode(p_attr.rpc_type);
E->get().rpc_methods.push_back(nd);
}
}
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 26d3aed702..075977b60f 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -360,8 +360,8 @@ void NativeScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
Set<MethodInfo> signals_;
while (script_data) {
- for (Map<StringName, NativeScriptDesc::Signal>::Element *S = script_data->signals_.front(); S; S = S->next()) {
- signals_.insert(S->get().signal);
+ for (const KeyValue<StringName, NativeScriptDesc::Signal> &S : script_data->signals_) {
+ signals_.insert(S.value.signal);
}
script_data = script_data->base_data;
@@ -401,8 +401,8 @@ void NativeScript::get_script_method_list(List<MethodInfo> *p_list) const {
Set<MethodInfo> methods;
while (script_data) {
- for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
- methods.insert(E->get().info);
+ for (const KeyValue<StringName, NativeScriptDesc::Method> &E : script_data->methods) {
+ methods.insert(E.value.info);
}
script_data = script_data->base_data;
@@ -431,9 +431,9 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
-const Vector<MultiplayerAPI::RPCConfig> NativeScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> NativeScript::get_rpc_methods() const {
NativeScriptDesc *script_data = get_script_desc();
- ERR_FAIL_COND_V(!script_data, Vector<MultiplayerAPI::RPCConfig>());
+ ERR_FAIL_COND_V(!script_data, Vector<Multiplayer::RPCConfig>());
return script_data->rpc_methods;
}
@@ -682,7 +682,7 @@ void NativeScriptInstance::get_property_list(List<PropertyInfo> *p_properties) c
ERR_CONTINUE(info.type < 0 || info.type >= Variant::VARIANT_MAX);
info.name = d["name"];
- ERR_CONTINUE(info.name == "");
+ ERR_CONTINUE(info.name.is_empty());
if (d.has("hint")) {
info.hint = PropertyHint(d["hint"].operator int64_t());
@@ -828,7 +828,7 @@ Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
-const Vector<MultiplayerAPI::RPCConfig> NativeScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> NativeScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
@@ -857,9 +857,9 @@ NativeScriptLanguage *NativeScriptLanguage::singleton;
void NativeScriptLanguage::_unload_stuff(bool p_reload) {
Map<String, Ref<GDNative>> erase_and_unload;
- for (Map<String, Map<StringName, NativeScriptDesc>>::Element *L = library_classes.front(); L; L = L->next()) {
- String lib_path = L->key();
- Map<StringName, NativeScriptDesc> classes = L->get();
+ for (KeyValue<String, Map<StringName, NativeScriptDesc>> &L : library_classes) {
+ String lib_path = L.key;
+ Map<StringName, NativeScriptDesc> classes = L.value;
if (p_reload) {
Map<String, Ref<GDNative>>::Element *E = library_gdnatives.find(lib_path);
@@ -890,9 +890,9 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
gdn = E->get();
}
- for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) {
+ for (KeyValue<StringName, NativeScriptDesc> &C : classes) {
// free property stuff first
- for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = C->get().properties.front(); P; P = P.next()) {
+ for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = C.value.properties.front(); P; P = P.next()) {
if (P.get().getter.free_func) {
P.get().getter.free_func(P.get().getter.method_data);
}
@@ -903,28 +903,28 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
}
// free method stuff
- for (Map<StringName, NativeScriptDesc::Method>::Element *M = C->get().methods.front(); M; M = M->next()) {
- if (M->get().method.free_func) {
- M->get().method.free_func(M->get().method.method_data);
+ for (const KeyValue<StringName, NativeScriptDesc::Method> &M : C.value.methods) {
+ if (M.value.method.free_func) {
+ M.value.method.free_func(M.value.method.method_data);
}
}
// free constructor/destructor
- if (C->get().create_func.free_func) {
- C->get().create_func.free_func(C->get().create_func.method_data);
+ if (C.value.create_func.free_func) {
+ C.value.create_func.free_func(C.value.create_func.method_data);
}
- if (C->get().destroy_func.free_func) {
- C->get().destroy_func.free_func(C->get().destroy_func.method_data);
+ if (C.value.destroy_func.free_func) {
+ C.value.destroy_func.free_func(C.value.destroy_func.method_data);
}
}
erase_and_unload.insert(lib_path, gdn);
}
- for (Map<String, Ref<GDNative>>::Element *E = erase_and_unload.front(); E; E = E->next()) {
- String lib_path = E->key();
- Ref<GDNative> gdn = E->get();
+ for (KeyValue<String, Ref<GDNative>> &E : erase_and_unload) {
+ String lib_path = E.key;
+ Ref<GDNative> gdn = E.value;
library_classes.erase(lib_path);
@@ -957,8 +957,8 @@ NativeScriptLanguage::NativeScriptLanguage() {
}
NativeScriptLanguage::~NativeScriptLanguage() {
- for (Map<String, Ref<GDNative>>::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
- Ref<GDNative> lib = L->get();
+ for (KeyValue<String, Ref<GDNative>> &L : NSL->library_gdnatives) {
+ Ref<GDNative> lib = L.value;
// only shut down valid libs, duh!
if (lib.is_valid()) {
// If it's a singleton-library then the gdnative module
@@ -1139,15 +1139,12 @@ void NativeScriptLanguage::profiling_start() {
MutexLock lock(mutex);
profile_data.clear();
- profiling = true;
#endif
}
void NativeScriptLanguage::profiling_stop() {
#ifdef DEBUG_ENABLED
MutexLock lock(mutex);
-
- profiling = false;
#endif
}
@@ -1157,15 +1154,15 @@ int NativeScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_a
int current = 0;
- for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
+ for (const KeyValue<StringName, ProfileData> &d : profile_data) {
if (current >= p_info_max) {
break;
}
- p_info_arr[current].call_count = d->get().call_count;
- p_info_arr[current].self_time = d->get().self_time;
- p_info_arr[current].total_time = d->get().total_time;
- p_info_arr[current].signature = d->get().signature;
+ p_info_arr[current].call_count = d.value.call_count;
+ p_info_arr[current].self_time = d.value.self_time;
+ p_info_arr[current].total_time = d.value.total_time;
+ p_info_arr[current].signature = d.value.signature;
current++;
}
@@ -1181,16 +1178,16 @@ int NativeScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, in
int current = 0;
- for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
+ for (const KeyValue<StringName, ProfileData> &d : profile_data) {
if (current >= p_info_max) {
break;
}
- if (d->get().last_frame_call_count) {
- p_info_arr[current].call_count = d->get().last_frame_call_count;
- p_info_arr[current].self_time = d->get().last_frame_self_time;
- p_info_arr[current].total_time = d->get().last_frame_total_time;
- p_info_arr[current].signature = d->get().signature;
+ if (d.value.last_frame_call_count) {
+ p_info_arr[current].call_count = d.value.last_frame_call_count;
+ p_info_arr[current].self_time = d.value.last_frame_self_time;
+ p_info_arr[current].total_time = d.value.last_frame_total_time;
+ p_info_arr[current].signature = d.value.signature;
current++;
}
}
@@ -1503,9 +1500,9 @@ void NativeScriptLanguage::unregister_script(NativeScript *script) {
if (L) {
Map<StringName, NativeScriptDesc> classes = L->get();
- for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) {
+ for (KeyValue<StringName, NativeScriptDesc> &C : classes) {
// free property stuff first
- for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = C->get().properties.front(); P; P = P.next()) {
+ for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element P = C.value.properties.front(); P; P = P.next()) {
if (P.get().getter.free_func) {
P.get().getter.free_func(P.get().getter.method_data);
}
@@ -1516,19 +1513,19 @@ void NativeScriptLanguage::unregister_script(NativeScript *script) {
}
// free method stuff
- for (Map<StringName, NativeScriptDesc::Method>::Element *M = C->get().methods.front(); M; M = M->next()) {
- if (M->get().method.free_func) {
- M->get().method.free_func(M->get().method.method_data);
+ for (const KeyValue<StringName, NativeScriptDesc::Method> &M : C.value.methods) {
+ if (M.value.method.free_func) {
+ M.value.method.free_func(M.value.method.method_data);
}
}
// free constructor/destructor
- if (C->get().create_func.free_func) {
- C->get().create_func.free_func(C->get().create_func.method_data);
+ if (C.value.create_func.free_func) {
+ C.value.create_func.free_func(C.value.create_func.method_data);
}
- if (C->get().destroy_func.free_func) {
- C->get().destroy_func.free_func(C->get().destroy_func.method_data);
+ if (C.value.destroy_func.free_func) {
+ C.value.destroy_func.free_func(C.value.destroy_func.method_data);
}
}
@@ -1548,14 +1545,14 @@ void NativeScriptLanguage::unregister_script(NativeScript *script) {
void NativeScriptLanguage::call_libraries_cb(const StringName &name) {
// library_gdnatives is modified only from the main thread, so it's safe not to use mutex here
- for (Map<String, Ref<GDNative>>::Element *L = library_gdnatives.front(); L; L = L->next()) {
- if (L->get().is_null()) {
+ for (KeyValue<String, Ref<GDNative>> &L : library_gdnatives) {
+ if (L.value.is_null()) {
continue;
}
- if (L->get()->is_initialized()) {
+ if (L.value->is_initialized()) {
void *proc_ptr;
- Error err = L->get()->get_symbol(L->get()->get_library()->get_symbol_prefix() + name, proc_ptr);
+ Error err = L.value->get_symbol(L.value->get_library()->get_symbol_prefix() + name, proc_ptr);
if (!err) {
((void (*)())proc_ptr)();
@@ -1584,13 +1581,13 @@ void NativeScriptLanguage::frame() {
{
MutexLock lock(mutex);
- for (Map<StringName, ProfileData>::Element *d = profile_data.front(); d; d = d->next()) {
- d->get().last_frame_call_count = d->get().frame_call_count;
- d->get().last_frame_self_time = d->get().frame_self_time;
- d->get().last_frame_total_time = d->get().frame_total_time;
- d->get().frame_call_count = 0;
- d->get().frame_self_time = 0;
- d->get().frame_total_time = 0;
+ for (KeyValue<StringName, ProfileData> &d : profile_data) {
+ d.value.last_frame_call_count = d.value.frame_call_count;
+ d.value.last_frame_self_time = d.value.frame_self_time;
+ d.value.last_frame_total_time = d.value.frame_total_time;
+ d.value.frame_call_count = 0;
+ d.value.frame_self_time = 0;
+ d.value.frame_total_time = 0;
}
}
#endif
@@ -1651,8 +1648,8 @@ void NativeReloadNode::_notification(int p_what) {
MutexLock lock(NSL->mutex);
NSL->_unload_stuff(true);
- for (Map<String, Ref<GDNative>>::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
- Ref<GDNative> gdn = L->get();
+ for (KeyValue<String, Ref<GDNative>> &L : NSL->library_gdnatives) {
+ Ref<GDNative> gdn = L.value;
if (gdn.is_null()) {
continue;
@@ -1685,8 +1682,8 @@ void NativeReloadNode::_notification(int p_what) {
MutexLock lock(NSL->mutex);
Set<StringName> libs_to_remove;
- for (Map<String, Ref<GDNative>>::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
- Ref<GDNative> gdn = L->get();
+ for (KeyValue<String, Ref<GDNative>> &L : NSL->library_gdnatives) {
+ Ref<GDNative> gdn = L.value;
if (gdn.is_null()) {
continue;
@@ -1703,24 +1700,24 @@ void NativeReloadNode::_notification(int p_what) {
}
if (!gdn->initialize()) {
- libs_to_remove.insert(L->key());
+ libs_to_remove.insert(L.key);
continue;
}
- NSL->library_classes.insert(L->key(), Map<StringName, NativeScriptDesc>());
+ NSL->library_classes.insert(L.key, Map<StringName, NativeScriptDesc>());
// here the library registers all the classes and stuff.
void *proc_ptr;
Error err = gdn->get_symbol(gdn->get_library()->get_symbol_prefix() + "nativescript_init", proc_ptr);
if (err != OK) {
- ERR_PRINT(String("No godot_nativescript_init in \"" + L->key() + "\" found").utf8().get_data());
+ ERR_PRINT(String("No godot_nativescript_init in \"" + L.key + "\" found").utf8().get_data());
} else {
- ((void (*)(void *))proc_ptr)((void *)&L->key());
+ ((void (*)(void *))proc_ptr)((void *)&L.key);
}
- for (Map<String, Set<NativeScript *>>::Element *U = NSL->library_script_users.front(); U; U = U->next()) {
- for (Set<NativeScript *>::Element *S = U->get().front(); S; S = S->next()) {
+ for (KeyValue<String, Set<NativeScript *>> &U : NSL->library_script_users) {
+ for (Set<NativeScript *>::Element *S = U.value.front(); S; S = S->next()) {
NativeScript *script = S->get();
if (script->placeholders.size() == 0) {
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 777a878660..2364c6c0f6 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -71,7 +71,7 @@ struct NativeScriptDesc {
};
Map<StringName, Method> methods;
- Vector<MultiplayerAPI::RPCConfig> rpc_methods;
+ Vector<Multiplayer::RPCConfig> rpc_methods;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
StringName base;
@@ -175,7 +175,7 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
String get_class_documentation() const;
String get_method_documentation(const StringName &p_method) const;
@@ -213,7 +213,7 @@ public:
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual ScriptLanguage *get_language();
@@ -269,7 +269,6 @@ private:
};
Map<StringName, ProfileData> profile_data;
- bool profiling = false;
public:
// These two maps must only be touched on the main thread
@@ -295,8 +294,6 @@ public:
return singleton;
}
- void _hacky_api_anchor();
-
_FORCE_INLINE_ void set_language_index(int p_idx) { lang_idx = p_idx; }
#ifndef NO_THREADS
diff --git a/modules/gdnative/net/SCsub b/modules/gdnative/net/SCsub
deleted file mode 100644
index b76500c003..0000000000
--- a/modules/gdnative/net/SCsub
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_gdnative")
-
-env_net = env_gdnative.Clone()
-
-has_webrtc = env_net["module_webrtc_enabled"]
-if has_webrtc:
- env_net.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"])
-
-env_net.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.cpp b/modules/gdnative/net/multiplayer_peer_gdnative.cpp
deleted file mode 100644
index 9908ed4533..0000000000
--- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*************************************************************************/
-/* multiplayer_peer_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "multiplayer_peer_gdnative.h"
-
-MultiplayerPeerGDNative::MultiplayerPeerGDNative() {
- interface = nullptr;
-}
-
-MultiplayerPeerGDNative::~MultiplayerPeerGDNative() {
-}
-
-void MultiplayerPeerGDNative::set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_interface) {
- interface = p_interface;
-}
-
-Error MultiplayerPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size);
-}
-
-Error MultiplayerPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
-}
-
-int MultiplayerPeerGDNative::get_max_packet_size() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_max_packet_size(interface->data);
-}
-
-int MultiplayerPeerGDNative::get_available_packet_count() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_packet_count(interface->data);
-}
-
-/* MultiplayerPeer */
-void MultiplayerPeerGDNative::set_transfer_channel(int p_channel) {
- ERR_FAIL_COND(interface == nullptr);
- return interface->set_transfer_channel(interface->data, p_channel);
-}
-
-int MultiplayerPeerGDNative::get_transfer_channel() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_transfer_channel(interface->data);
-}
-
-void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
- ERR_FAIL_COND(interface == nullptr);
- interface->set_transfer_mode(interface->data, (godot_int)p_mode);
-}
-
-MultiplayerPeer::TransferMode MultiplayerPeerGDNative::get_transfer_mode() const {
- ERR_FAIL_COND_V(interface == nullptr, TRANSFER_MODE_UNRELIABLE);
- return (TransferMode)interface->get_transfer_mode(interface->data);
-}
-
-void MultiplayerPeerGDNative::set_target_peer(int p_peer_id) {
- ERR_FAIL_COND(interface == nullptr);
- interface->set_target_peer(interface->data, p_peer_id);
-}
-
-int MultiplayerPeerGDNative::get_packet_peer() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_packet_peer(interface->data);
-}
-
-bool MultiplayerPeerGDNative::is_server() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->is_server(interface->data);
-}
-
-void MultiplayerPeerGDNative::poll() {
- ERR_FAIL_COND(interface == nullptr);
- interface->poll(interface->data);
-}
-
-int MultiplayerPeerGDNative::get_unique_id() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_unique_id(interface->data);
-}
-
-void MultiplayerPeerGDNative::set_refuse_new_connections(bool p_enable) {
- ERR_FAIL_COND(interface == nullptr);
- interface->set_refuse_new_connections(interface->data, p_enable);
-}
-
-bool MultiplayerPeerGDNative::is_refusing_new_connections() const {
- ERR_FAIL_COND_V(interface == nullptr, true);
- return interface->is_refusing_new_connections(interface->data);
-}
-
-MultiplayerPeer::ConnectionStatus MultiplayerPeerGDNative::get_connection_status() const {
- ERR_FAIL_COND_V(interface == nullptr, CONNECTION_DISCONNECTED);
- return (ConnectionStatus)interface->get_connection_status(interface->data);
-}
-
-void MultiplayerPeerGDNative::_bind_methods() {
- ADD_PROPERTY_DEFAULT("transfer_channel", 0);
- ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_UNRELIABLE);
- ADD_PROPERTY_DEFAULT("refuse_new_connections", true);
-}
-
-extern "C" {
-
-void GDAPI godot_net_bind_multiplayer_peer(godot_object *p_obj, const godot_net_multiplayer_peer *p_impl) {
- ((MultiplayerPeerGDNative *)p_obj)->set_native_multiplayer_peer(p_impl);
-}
-}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gdnative/net/multiplayer_peer_gdnative.h
deleted file mode 100644
index ab084faae6..0000000000
--- a/modules/gdnative/net/multiplayer_peer_gdnative.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*************************************************************************/
-/* multiplayer_peer_gdnative.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef MULTIPLAYER_PEER_GDNATIVE_H
-#define MULTIPLAYER_PEER_GDNATIVE_H
-
-#include "core/io/multiplayer_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
-
-class MultiplayerPeerGDNative : public MultiplayerPeer {
- GDCLASS(MultiplayerPeerGDNative, MultiplayerPeer);
-
-protected:
- static void _bind_methods();
- const godot_net_multiplayer_peer *interface;
-
-public:
- MultiplayerPeerGDNative();
- ~MultiplayerPeerGDNative();
-
- /* Sets the interface implementation from GDNative */
- void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
-
- /* Specific to PacketPeer */
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const override;
- virtual int get_available_packet_count() const override;
-
- /* Specific to MultiplayerPeer */
- virtual void set_transfer_channel(int p_channel) override;
- virtual int get_transfer_channel() const override;
- virtual void set_transfer_mode(TransferMode p_mode) override;
- virtual TransferMode get_transfer_mode() const override;
- virtual void set_target_peer(int p_peer_id) override;
-
- virtual int get_packet_peer() const override;
-
- virtual bool is_server() const override;
-
- virtual void poll() override;
-
- virtual int get_unique_id() const override;
-
- virtual void set_refuse_new_connections(bool p_enable) override;
- virtual bool is_refusing_new_connections() const override;
-
- virtual ConnectionStatus get_connection_status() const override;
-};
-
-#endif // MULTIPLAYER_PEER_GDNATIVE_H
diff --git a/modules/gdnative/net/register_types.cpp b/modules/gdnative/net/register_types.cpp
deleted file mode 100644
index 46c383e5ae..0000000000
--- a/modules/gdnative/net/register_types.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*************************************************************************/
-/* register_types.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "register_types.h"
-#include "multiplayer_peer_gdnative.h"
-#include "packet_peer_gdnative.h"
-#include "stream_peer_gdnative.h"
-
-void register_net_types() {
- GDREGISTER_CLASS(MultiplayerPeerGDNative);
- GDREGISTER_CLASS(PacketPeerGDNative);
- GDREGISTER_CLASS(StreamPeerGDNative);
-}
-
-void unregister_net_types() {
-}
diff --git a/modules/gdnative/net/register_types.h b/modules/gdnative/net/register_types.h
deleted file mode 100644
index c99c6f6fbf..0000000000
--- a/modules/gdnative/net/register_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef NET_REGISTER_TYPES_H
-#define NET_REGISTER_TYPES_H
-
-void register_net_types();
-void unregister_net_types();
-
-#endif // NET_REGISTER_TYPES_H
diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gdnative/net/stream_peer_gdnative.h
deleted file mode 100644
index dd5abceb83..0000000000
--- a/modules/gdnative/net/stream_peer_gdnative.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*************************************************************************/
-/* stream_peer_gdnative.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef STREAM_PEER_GDNATIVE_H
-#define STREAM_PEER_GDNATIVE_H
-
-#include "core/io/stream_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
-
-class StreamPeerGDNative : public StreamPeer {
- GDCLASS(StreamPeerGDNative, StreamPeer);
-
-protected:
- static void _bind_methods();
- const godot_net_stream_peer *interface;
-
-public:
- StreamPeerGDNative();
- ~StreamPeerGDNative();
-
- /* Sets the interface implementation from GDNative */
- void set_native_stream_peer(const godot_net_stream_peer *p_interface);
-
- /* Specific to StreamPeer */
- Error put_data(const uint8_t *p_data, int p_bytes) override;
- Error put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) override;
- Error get_data(uint8_t *p_buffer, int p_bytes) override;
- Error get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) override;
- int get_available_bytes() const override;
-};
-
-#endif // STREAM_PEER_GDNATIVE_H
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index ed1c0af3ed..feae81397e 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -100,7 +100,7 @@ String PluginScriptInstance::to_string(bool *r_valid) {
return str_ret;
}
-const Vector<MultiplayerAPI::RPCConfig> PluginScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> PluginScriptInstance::get_rpc_methods() const {
return _script->get_rpc_methods();
}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 25b62ae8ab..81e711bafc 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -69,9 +69,7 @@ public:
virtual ScriptLanguage *get_language();
- void set_path(const String &p_path);
-
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual void refcount_incremented();
virtual bool refcount_decremented();
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 5380858582..5bda9e1d53 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -232,8 +232,7 @@ bool PluginScript::instance_has(const Object *p_this) const {
}
bool PluginScript::has_source_code() const {
- bool has = _source != "";
- return has;
+ return !_source.is_empty();
}
String PluginScript::get_source_code() const {
@@ -257,11 +256,11 @@ Error PluginScript::reload(bool p_keep_state) {
_valid = false;
String basedir = _path;
- if (basedir == "") {
+ if (basedir.is_empty()) {
basedir = get_path();
}
- if (basedir != "") {
+ if (!basedir.is_empty()) {
basedir = basedir.get_base_dir();
}
@@ -334,9 +333,9 @@ Error PluginScript::reload(bool p_keep_state) {
// rpc_mode is passed as an optional field and is not part of MethodInfo
Variant var = v["rpc_mode"];
if (var != Variant()) {
- MultiplayerAPI::RPCConfig nd;
+ Multiplayer::RPCConfig nd;
nd.name = mi.name;
- nd.rpc_mode = MultiplayerAPI::RPCMode(int(var));
+ nd.rpc_mode = Multiplayer::RPCMode(int(var));
// TODO Transfer Channel
if (_rpc_methods.find(nd) == -1) {
_rpc_methods.push_back(nd);
@@ -345,7 +344,7 @@ Error PluginScript::reload(bool p_keep_state) {
}
// Sort so we are 100% that they are always the same.
- _rpc_methods.sort_custom<MultiplayerAPI::SortRPCConfig>();
+ _rpc_methods.sort_custom<Multiplayer::SortRPCConfig>();
Array *signals = (Array *)&manifest.signals;
for (int i = 0; i < signals->size(); ++i) {
@@ -361,13 +360,6 @@ Error PluginScript::reload(bool p_keep_state) {
_properties_default_values[pi.name] = v["default_value"];
}
-#ifdef TOOLS_ENABLED
-/*for (Set<PlaceHolderScriptInstance*>::Element *E=placeholders.front();E;E=E->next()) {
-
- _update_placeholder(E->get());
- }*/
-#endif
-
FREE_SCRIPT_MANIFEST(manifest);
return OK;
#undef FREE_SCRIPT_MANIFEST
@@ -484,7 +476,7 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
-const Vector<MultiplayerAPI::RPCConfig> PluginScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> PluginScript::get_rpc_methods() const {
return _rpc_methods;
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 838195147f..1a12a130d1 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -61,7 +61,7 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
- Vector<MultiplayerAPI::RPCConfig> _rpc_methods;
+ Vector<Multiplayer::RPCConfig> _rpc_methods;
Set<Object *> _instances;
//exported members
@@ -136,7 +136,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
PluginScript();
void init(PluginScriptLanguage *language);
diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
index 7faacfdcb9..c4fbff69f0 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -44,9 +44,9 @@
static List<PluginScriptLanguage *> pluginscript_languages;
static Error _check_language_desc(const godot_pluginscript_language_desc *desc) {
- ERR_FAIL_COND_V(!desc->name || desc->name == String(), ERR_BUG);
- ERR_FAIL_COND_V(!desc->type || desc->type == String(), ERR_BUG);
- ERR_FAIL_COND_V(!desc->extension || desc->extension == String(), ERR_BUG);
+ ERR_FAIL_COND_V(!desc->name, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->type, ERR_BUG);
+ ERR_FAIL_COND_V(!desc->extension, ERR_BUG);
ERR_FAIL_COND_V(!desc->recognized_extensions || !desc->recognized_extensions[0], ERR_BUG);
ERR_FAIL_COND_V(!desc->init, ERR_BUG);
ERR_FAIL_COND_V(!desc->finish, ERR_BUG);
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index e4c2b20224..a4ab5663ef 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -35,7 +35,6 @@
#include "gdnative.h"
#include "nativescript/register_types.h"
-#include "net/register_types.h"
#include "pluginscript/register_types.h"
#include "videodecoder/register_types.h"
@@ -265,7 +264,6 @@ void register_gdnative_types() {
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
- register_net_types();
register_nativescript_types();
register_pluginscript_types();
register_videodecoder_types();
@@ -329,7 +327,6 @@ void unregister_gdnative_types() {
unregister_videodecoder_types();
unregister_pluginscript_types();
unregister_nativescript_types();
- unregister_net_types();
memdelete(GDNativeCallRegistry::singleton);
diff --git a/modules/gdnative/text/SCsub b/modules/gdnative/text/SCsub
deleted file mode 100644
index 0b2db3b504..0000000000
--- a/modules/gdnative/text/SCsub
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_gdnative")
-
-env_gdnative.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gdnative/text/config.py b/modules/gdnative/text/config.py
deleted file mode 100644
index d22f9454ed..0000000000
--- a/modules/gdnative/text/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return True
-
-
-def configure(env):
- pass
diff --git a/modules/gdnative/text/register_types.cpp b/modules/gdnative/text/register_types.cpp
deleted file mode 100644
index 67385d2fbf..0000000000
--- a/modules/gdnative/text/register_types.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*************************************************************************/
-/* register_types.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "register_types.h"
-#include "text_server_gdnative.h"
-
-void register_text_server_gdn_types() {}
-
-void unregister_text_server_gdn_types() {}
diff --git a/modules/gdnative/text/register_types.h b/modules/gdnative/text/register_types.h
deleted file mode 100644
index cd4f2a3089..0000000000
--- a/modules/gdnative/text/register_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef TEXT_REGISTER_TYPES_H
-#define TEXT_REGISTER_TYPES_H
-
-void register_text_server_gdn_types();
-void unregister_text_server_gdn_types();
-
-#endif // TEXT_REGISTER_TYPES_H
diff --git a/modules/gdnative/text/text_server_gdnative.cpp b/modules/gdnative/text/text_server_gdnative.cpp
deleted file mode 100644
index 39db8ae636..0000000000
--- a/modules/gdnative/text/text_server_gdnative.cpp
+++ /dev/null
@@ -1,1086 +0,0 @@
-/*************************************************************************/
-/* text_server_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "text_server_gdnative.h"
-
-bool TextServerGDNative::has_feature(Feature p_feature) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->has_feature(data, (godot_int)p_feature);
-}
-
-String TextServerGDNative::get_name() const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->get_name(data);
- String name = *(String *)&result;
- godot_string_destroy(&result);
- return name;
-}
-
-void TextServerGDNative::free(RID p_rid) {
- ERR_FAIL_COND(interface == nullptr);
- interface->free(data, (godot_rid *)&p_rid);
-}
-
-bool TextServerGDNative::has(RID p_rid) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->has(data, (godot_rid *)&p_rid);
-}
-
-bool TextServerGDNative::load_support_data(const String &p_filename) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->load_support_data(data, (godot_string *)&p_filename);
-}
-
-#ifdef TOOLS_ENABLED
-
-String TextServerGDNative::get_support_data_filename() {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->get_support_data_filename(data);
- String name = *(String *)&result;
- godot_string_destroy(&result);
- return name;
-}
-
-String TextServerGDNative::get_support_data_info() {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->get_support_data_info(data);
- String info = *(String *)&result;
- godot_string_destroy(&result);
- return info;
-}
-
-bool TextServerGDNative::save_support_data(const String &p_filename) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->save_support_data(data, (godot_string *)&p_filename);
-}
-
-#endif
-
-bool TextServerGDNative::is_locale_right_to_left(const String &p_locale) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->is_locale_right_to_left(data, (godot_string *)&p_locale);
-}
-
-int32_t TextServerGDNative::name_to_tag(const String &p_name) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->name_to_tag(data, (godot_string *)&p_name);
-}
-
-String TextServerGDNative::tag_to_name(int32_t p_tag) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->tag_to_name(data, p_tag);
- String name = *(String *)&result;
- godot_string_destroy(&result);
- return name;
-}
-
-/*************************************************************************/
-/* Font */
-/*************************************************************************/
-
-RID TextServerGDNative::create_font() {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_font(data);
- RID rid = *(RID *)&result;
- return rid;
-}
-
-void TextServerGDNative::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_data(data, (godot_rid *)&p_font_rid, (const godot_packed_byte_array *)&p_data);
-}
-
-void TextServerGDNative::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_data_ptr(data, (godot_rid *)&p_font_rid, p_data_ptr, p_data_size);
-}
-
-void TextServerGDNative::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_antialiased(data, (godot_rid *)&p_font_rid, p_antialiased);
-}
-
-bool TextServerGDNative::font_is_antialiased(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_antialiased(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid, p_msdf);
-}
-
-bool TextServerGDNative::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_multichannel_signed_distance_field(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_msdf_pixel_range(data, (godot_rid *)&p_font_rid, p_msdf_pixel_range);
-}
-
-int TextServerGDNative::font_get_msdf_pixel_range(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_msdf_pixel_range(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_msdf_size(data, (godot_rid *)&p_font_rid, p_msdf_size);
-}
-
-int TextServerGDNative::font_get_msdf_size(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_msdf_size(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_fixed_size(data, (godot_rid *)&p_font_rid, p_fixed_size);
-}
-
-int TextServerGDNative::font_get_fixed_size(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_fixed_size(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_force_autohinter(data, (godot_rid *)&p_font_rid, p_force_autohinter);
-}
-
-bool TextServerGDNative::font_is_force_autohinter(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_force_autohinter(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_hinting(data, (godot_rid *)&p_font_rid, (godot_int)p_hinting);
-}
-
-TextServer::Hinting TextServerGDNative::font_get_hinting(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, TextServer::HINTING_NONE);
- return (TextServer::Hinting)interface->font_get_hinting(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_variation_coordinates(data, (godot_rid *)&p_font_rid, (const godot_dictionary *)&p_variation_coordinates);
-}
-
-Dictionary TextServerGDNative::font_get_variation_coordinates(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, Dictionary());
- godot_dictionary result = interface->font_get_variation_coordinates(data, (godot_rid *)&p_font_rid);
- Dictionary dict = *(Dictionary *)&result;
- godot_dictionary_destroy(&result);
- return dict;
-}
-
-void TextServerGDNative::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_oversampling(data, (godot_rid *)&p_font_rid, p_oversampling);
-}
-
-real_t TextServerGDNative::font_get_oversampling(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_oversampling(data, (godot_rid *)&p_font_rid);
-}
-
-Array TextServerGDNative::font_get_size_cache_list(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, Array());
- godot_array result = interface->font_get_size_cache_list(data, (godot_rid *)&p_font_rid);
- Array list = *(Array *)&result;
- godot_array_destroy(&result);
- return list;
-}
-
-void TextServerGDNative::font_clear_size_cache(RID p_font_rid) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_clear_size_cache(data, (godot_rid *)&p_font_rid);
-}
-
-void TextServerGDNative::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_size_cache(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
-}
-
-void TextServerGDNative::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_ascent(data, (godot_rid *)&p_font_rid, p_size, p_ascent);
-}
-
-real_t TextServerGDNative::font_get_ascent(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_ascent(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_descent(data, (godot_rid *)&p_font_rid, p_size, p_descent);
-}
-
-real_t TextServerGDNative::font_get_descent(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_descent(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_underline_position(data, (godot_rid *)&p_font_rid, p_size, p_underline_position);
-}
-
-real_t TextServerGDNative::font_get_underline_position(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_underline_position(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_underline_thickness(data, (godot_rid *)&p_font_rid, p_size, p_underline_thickness);
-}
-
-real_t TextServerGDNative::font_get_underline_thickness(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_underline_thickness(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_scale(data, (godot_rid *)&p_font_rid, p_size, p_scale);
-}
-
-real_t TextServerGDNative::font_get_scale(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_scale(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing, p_value);
-}
-
-int TextServerGDNative::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_spacing(data, (godot_rid *)&p_font_rid, p_size, (godot_int)p_spacing);
-}
-
-int TextServerGDNative::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, -1);
- return interface->font_get_texture_count(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
-}
-
-void TextServerGDNative::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_clear_textures(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
-}
-
-void TextServerGDNative::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_texture(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
-}
-
-void TextServerGDNative::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_object *)p_image.ptr());
-}
-
-Ref<Image> TextServerGDNative::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- ERR_FAIL_COND_V(interface == nullptr, Ref<Image>());
- godot_object *result = interface->font_get_texture_image(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
- return Ref<Image>((Image *)result);
-}
-
-void TextServerGDNative::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index, (const godot_packed_int32_array *)&p_offset);
-}
-
-PackedInt32Array TextServerGDNative::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- ERR_FAIL_COND_V(interface == nullptr, PackedInt32Array());
- godot_packed_int32_array result = interface->font_get_texture_offsets(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_texture_index);
- PackedInt32Array offset = *(PackedInt32Array *)&result;
- godot_packed_int32_array_destroy(&result);
- return offset;
-}
-
-Array TextServerGDNative::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, Array());
- godot_array result = interface->font_get_glyph_list(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
- Array list = *(Array *)&result;
- godot_array_destroy(&result);
- return list;
-}
-
-void TextServerGDNative::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_clear_glyphs(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size);
-}
-
-void TextServerGDNative::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
-}
-
-Vector2 TextServerGDNative::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph);
- Vector2 adv = *(Vector2 *)&result;
- return adv;
-}
-
-void TextServerGDNative::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_glyph_advance(data, (godot_rid *)&p_font_rid, p_size, p_glyph, (const godot_vector2 *)&p_advance);
-}
-
-Vector2 TextServerGDNative::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
- Vector2 off = *(Vector2 *)&result;
- return off;
-}
-
-void TextServerGDNative::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_glyph_offset(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_offset);
-}
-
-Vector2 TextServerGDNative::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
- Vector2 sz = *(Vector2 *)&result;
- return sz;
-}
-
-void TextServerGDNative::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_glyph_size(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_vector2 *)&p_gl_size);
-}
-
-Rect2 TextServerGDNative::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- ERR_FAIL_COND_V(interface == nullptr, Rect2());
- godot_rect2 result = interface->font_get_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
- Rect2 uv = *(Rect2 *)&result;
- return uv;
-}
-
-void TextServerGDNative::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_glyph_uv_rect(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, (const godot_rect2 *)&p_uv_rect);
-}
-
-int TextServerGDNative::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- ERR_FAIL_COND_V(interface == nullptr, -1);
- return interface->font_get_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph);
-}
-
-void TextServerGDNative::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_glyph_texture_idx(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_glyph, p_texture_idx);
-}
-
-bool TextServerGDNative::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_glyph_contours(data, (godot_rid *)&p_font_rid, p_size, p_index, (godot_packed_vector3_array *)&r_points, (godot_packed_int32_array *)&r_contours, &r_orientation);
-}
-
-Array TextServerGDNative::font_get_kerning_list(RID p_font_rid, int p_size) const {
- ERR_FAIL_COND_V(interface == nullptr, Array());
- godot_array result = interface->font_get_kerning_list(data, (godot_rid *)&p_font_rid, p_size);
- Array list = *(Array *)&result;
- godot_array_destroy(&result);
- return list;
-}
-
-void TextServerGDNative::font_clear_kerning_map(RID p_font_rid, int p_size) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_clear_kerning_map(data, (godot_rid *)&p_font_rid, p_size);
-}
-
-void TextServerGDNative::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair);
-}
-
-void TextServerGDNative::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair, (const godot_vector2 *)&p_kerning);
-}
-
-Vector2 TextServerGDNative::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2());
- godot_vector2 result = interface->font_get_kerning(data, (godot_rid *)&p_font_rid, p_size, (const godot_vector2i *)&p_glyph_pair);
- Vector2 kern = *(Vector2 *)&result;
- return kern;
-}
-
-int32_t TextServerGDNative::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->font_get_glyph_index(data, (godot_rid *)&p_font_rid, p_size, p_char, p_variation_selector);
-}
-
-bool TextServerGDNative::font_has_char(RID p_font_rid, char32_t p_char) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_has_char(data, (godot_rid *)&p_font_rid, p_char);
-}
-
-String TextServerGDNative::font_get_supported_chars(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->font_get_supported_chars(data, (godot_rid *)&p_font_rid);
- String chars = *(String *)&result;
- godot_string_destroy(&result);
- return chars;
-}
-
-void TextServerGDNative::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_render_range(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_start, p_end);
-}
-
-void TextServerGDNative::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_render_glyph(data, (godot_rid *)&p_font_rid, (const godot_vector2i *)&p_size, p_index);
-}
-
-void TextServerGDNative::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_draw_glyph(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
-}
-
-void TextServerGDNative::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_draw_glyph_outline(data, (godot_rid *)&p_font_rid, (godot_rid *)&p_canvas, p_size, p_outline_size, (const godot_vector2 *)&p_pos, p_index, (const godot_color *)&p_color);
-}
-
-bool TextServerGDNative::font_is_language_supported(RID p_font_rid, const String &p_language) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_language_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
-}
-
-void TextServerGDNative::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language, p_supported);
-}
-
-bool TextServerGDNative::font_get_language_support_override(RID p_font_rid, const String &p_language) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
-}
-
-void TextServerGDNative::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_language_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_language);
-}
-
-Vector<String> TextServerGDNative::font_get_language_support_overrides(RID p_font_rid) {
- ERR_FAIL_COND_V(interface == nullptr, PackedStringArray());
- godot_packed_string_array result = interface->font_get_language_support_overrides(data, (godot_rid *)&p_font_rid);
- PackedStringArray list = *(PackedStringArray *)&result;
- godot_packed_string_array_destroy(&result);
- return list;
-}
-
-bool TextServerGDNative::font_is_script_supported(RID p_font_rid, const String &p_script) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_is_script_supported(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
-}
-
-void TextServerGDNative::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script, p_supported);
-}
-
-bool TextServerGDNative::font_get_script_support_override(RID p_font_rid, const String &p_script) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->font_get_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
-}
-
-void TextServerGDNative::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_remove_script_support_override(data, (godot_rid *)&p_font_rid, (const godot_string *)&p_script);
-}
-
-Vector<String> TextServerGDNative::font_get_script_support_overrides(RID p_font_rid) {
- ERR_FAIL_COND_V(interface == nullptr, PackedStringArray());
- godot_packed_string_array result = interface->font_get_script_support_overrides(data, (godot_rid *)&p_font_rid);
- PackedStringArray list = *(PackedStringArray *)&result;
- godot_packed_string_array_destroy(&result);
- return list;
-}
-
-Dictionary TextServerGDNative::font_supported_feature_list(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, Dictionary());
- godot_dictionary result = interface->font_supported_feature_list(data, (godot_rid *)&p_font_rid);
- Dictionary dict = *(Dictionary *)&result;
- godot_dictionary_destroy(&result);
- return dict;
-}
-
-Dictionary TextServerGDNative::font_supported_variation_list(RID p_font_rid) const {
- ERR_FAIL_COND_V(interface == nullptr, Dictionary());
- godot_dictionary result = interface->font_supported_variation_list(data, (godot_rid *)&p_font_rid);
- Dictionary dict = *(Dictionary *)&result;
- godot_dictionary_destroy(&result);
- return dict;
-}
-
-real_t TextServerGDNative::font_get_global_oversampling() const {
- ERR_FAIL_COND_V(interface == nullptr, 0.0f);
- return interface->font_get_global_oversampling(data);
-}
-
-void TextServerGDNative::font_set_global_oversampling(real_t p_oversampling) {
- ERR_FAIL_COND(interface == nullptr);
- interface->font_set_global_oversampling(data, p_oversampling);
-}
-
-/*************************************************************************/
-/* Shaped text buffer interface */
-/*************************************************************************/
-
-RID TextServerGDNative::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->create_shaped_text(data, (godot_int)p_direction, (godot_int)p_orientation);
- RID rid = *(RID *)&result;
- return rid;
-}
-
-void TextServerGDNative::shaped_text_clear(RID p_shaped) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_clear(data, (godot_rid *)&p_shaped);
-}
-
-void TextServerGDNative::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_set_direction(data, (godot_rid *)&p_shaped, (godot_int)p_direction);
-}
-
-TextServer::Direction TextServerGDNative::shaped_text_get_direction(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, TextServer::DIRECTION_LTR);
- return (TextServer::Direction)interface->shaped_text_get_direction(data, (godot_rid *)&p_shaped);
-}
-
-void TextServerGDNative::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_set_orientation(data, (godot_rid *)&p_shaped, (godot_int)p_orientation);
-}
-
-TextServer::Orientation TextServerGDNative::shaped_text_get_orientation(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, TextServer::ORIENTATION_HORIZONTAL);
- return (TextServer::Orientation)interface->shaped_text_get_orientation(data, (godot_rid *)&p_shaped);
-}
-
-void TextServerGDNative::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_set_bidi_override(data, (godot_rid *)&p_shaped, (const godot_packed_vector2i_array *)&p_override);
-}
-
-void TextServerGDNative::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_set_preserve_invalid(data, (godot_rid *)&p_shaped, p_enabled);
-}
-
-bool TextServerGDNative::shaped_text_get_preserve_invalid(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return (TextServer::Orientation)interface->shaped_text_get_preserve_invalid(data, (godot_rid *)&p_shaped);
-}
-
-void TextServerGDNative::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_set_preserve_control(data, (godot_rid *)&p_shaped, p_enabled);
-}
-
-bool TextServerGDNative::shaped_text_get_preserve_control(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return (TextServer::Orientation)interface->shaped_text_get_preserve_control(data, (godot_rid *)&p_shaped);
-}
-
-bool TextServerGDNative::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_add_string(data, (godot_rid *)&p_shaped, (const godot_string *)&p_text, (const godot_rid **)p_fonts.ptr(), p_size, (const godot_dictionary *)&p_opentype_features, (const godot_string *)&p_language);
-}
-
-bool TextServerGDNative::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_add_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align, p_length);
-}
-
-bool TextServerGDNative::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_resize_object(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key, (const godot_vector2 *)&p_size, (godot_int)p_inline_align);
-}
-
-RID TextServerGDNative::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->shaped_text_substr(data, (godot_rid *)&p_shaped, (godot_int)p_start, (godot_int)p_length);
- RID rid = *(RID *)&result;
- return rid;
-}
-
-RID TextServerGDNative::shaped_text_get_parent(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, RID());
- godot_rid result = interface->shaped_text_get_parent(data, (godot_rid *)&p_shaped);
- RID rid = *(RID *)&result;
- return rid;
-}
-
-real_t TextServerGDNative::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t p_jst_flags) {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_fit_to_width(data, (godot_rid *)&p_shaped, p_width, p_jst_flags);
-}
-
-real_t TextServerGDNative::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_tab_align(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_tab_stops);
-}
-
-bool TextServerGDNative::shaped_text_shape(RID p_shaped) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_shape(data, (godot_rid *)&p_shaped);
-}
-
-bool TextServerGDNative::shaped_text_update_breaks(RID p_shaped) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_update_breaks(data, (godot_rid *)&p_shaped);
-}
-
-bool TextServerGDNative::shaped_text_update_justification_ops(RID p_shaped) {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_update_justification_ops(data, (godot_rid *)&p_shaped);
-}
-
-void TextServerGDNative::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
- ERR_FAIL_COND(interface == nullptr);
- interface->shaped_text_overrun_trim_to_width(data, (godot_rid *)&p_shaped_line, p_width, p_trim_flags);
-};
-
-bool TextServerGDNative::shaped_text_is_ready(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->shaped_text_is_ready(data, (godot_rid *)&p_shaped);
-}
-
-Vector<TextServer::Glyph> TextServerGDNative::shaped_text_get_glyphs(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector<TextServer::Glyph>());
- godot_packed_glyph_array result = interface->shaped_text_get_glyphs(data, (godot_rid *)&p_shaped);
- Vector<TextServer::Glyph> glyphs = *(Vector<TextServer::Glyph> *)&result;
- godot_packed_glyph_array_destroy(&result);
- return glyphs;
-}
-
-Vector2i TextServerGDNative::shaped_text_get_range(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector2i());
- godot_vector2i result = interface->shaped_text_get_range(data, (godot_rid *)&p_shaped);
- Vector2i range = *(Vector2i *)&result;
- return range;
-}
-
-Vector<TextServer::Glyph> TextServerGDNative::shaped_text_sort_logical(RID p_shaped) {
- ERR_FAIL_COND_V(interface == nullptr, Vector<TextServer::Glyph>());
- godot_packed_glyph_array result = interface->shaped_text_sort_logical(data, (godot_rid *)&p_shaped);
- Vector<TextServer::Glyph> glyphs = *(Vector<TextServer::Glyph> *)&result;
- godot_packed_glyph_array_destroy(&result);
- return glyphs;
-}
-
-Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start, bool p_once, uint8_t p_break_flags) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
- if (interface->shaped_text_get_line_breaks_adv != nullptr) {
- godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks_adv(data, (godot_rid *)&p_shaped, (godot_packed_float32_array *)&p_width, p_start, p_once, p_break_flags);
- Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
- godot_packed_vector2i_array_destroy(&result);
- return breaks;
- } else {
- return TextServer::shaped_text_get_line_breaks_adv(p_shaped, p_width, p_break_flags);
- }
-}
-
-Vector<Vector2i> TextServerGDNative::shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start, uint8_t p_break_flags) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
- if (interface->shaped_text_get_line_breaks != nullptr) {
- godot_packed_vector2i_array result = interface->shaped_text_get_line_breaks(data, (godot_rid *)&p_shaped, p_width, p_start, p_break_flags);
- Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
- godot_packed_vector2i_array_destroy(&result);
- return breaks;
- } else {
- return TextServer::shaped_text_get_line_breaks(p_shaped, p_width, p_break_flags);
- }
-}
-
-Vector<Vector2i> TextServerGDNative::shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags) const {
- ERR_FAIL_COND_V(interface == nullptr, Vector<Vector2i>());
- if (interface->shaped_text_get_word_breaks != nullptr) {
- godot_packed_vector2i_array result = interface->shaped_text_get_word_breaks(data, (godot_rid *)&p_shaped, p_grapheme_flags);
- Vector<Vector2i> breaks = *(Vector<Vector2i> *)&result;
- godot_packed_vector2i_array_destroy(&result);
- return breaks;
- } else {
- return TextServer::shaped_text_get_word_breaks(p_shaped, p_grapheme_flags);
- }
-}
-
-Array TextServerGDNative::shaped_text_get_objects(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, Array());
- godot_array result = interface->shaped_text_get_objects(data, (godot_rid *)&p_shaped);
- Array rect = *(Array *)&result;
- return rect;
-}
-
-Rect2 TextServerGDNative::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- ERR_FAIL_COND_V(interface == nullptr, Rect2());
- godot_rect2 result = interface->shaped_text_get_object_rect(data, (godot_rid *)&p_shaped, (const godot_variant *)&p_key);
- Rect2 rect = *(Rect2 *)&result;
- return rect;
-}
-
-Size2 TextServerGDNative::shaped_text_get_size(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, Size2());
- godot_vector2 result = interface->shaped_text_get_size(data, (godot_rid *)&p_shaped);
- Size2 size = *(Size2 *)&result;
- return size;
-}
-
-real_t TextServerGDNative::shaped_text_get_ascent(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_get_ascent(data, (godot_rid *)&p_shaped);
-}
-
-real_t TextServerGDNative::shaped_text_get_descent(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_get_descent(data, (godot_rid *)&p_shaped);
-}
-
-real_t TextServerGDNative::shaped_text_get_width(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_get_width(data, (godot_rid *)&p_shaped);
-}
-
-real_t TextServerGDNative::shaped_text_get_underline_position(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_get_underline_position(data, (godot_rid *)&p_shaped);
-}
-
-real_t TextServerGDNative::shaped_text_get_underline_thickness(RID p_shaped) const {
- ERR_FAIL_COND_V(interface == nullptr, 0.f);
- return interface->shaped_text_get_underline_thickness(data, (godot_rid *)&p_shaped);
-}
-
-String TextServerGDNative::format_number(const String &p_string, const String &p_language) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- godot_string result = interface->format_number(data, (const godot_string *)&p_string, (const godot_string *)&p_language);
- if (interface->format_number == nullptr) {
- return p_string;
- }
- String ret = *(String *)&result;
- godot_string_destroy(&result);
- return ret;
-}
-
-String TextServerGDNative::parse_number(const String &p_string, const String &p_language) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- if (interface->parse_number == nullptr) {
- return p_string;
- }
- godot_string result = interface->parse_number(data, (const godot_string *)&p_string, (const godot_string *)&p_language);
- String ret = *(String *)&result;
- godot_string_destroy(&result);
- return ret;
-}
-
-String TextServerGDNative::percent_sign(const String &p_language) const {
- ERR_FAIL_COND_V(interface == nullptr, String());
- if (interface->percent_sign == nullptr) {
- return "%";
- }
- godot_string result = interface->percent_sign(data, (const godot_string *)&p_language);
- String ret = *(String *)&result;
- godot_string_destroy(&result);
- return ret;
-}
-
-TextServer *TextServerGDNative::create_func(Error &r_error, void *p_user_data) {
- const godot_text_interface_gdnative *interface = (const godot_text_interface_gdnative *)p_user_data;
- r_error = OK;
-
- TextServerGDNative *server = memnew(TextServerGDNative());
- server->interface = interface;
- server->data = interface->constructor((godot_object *)server);
-
- return server;
-}
-
-TextServerGDNative::TextServerGDNative() {
- data = nullptr;
- interface = nullptr;
-}
-
-TextServerGDNative::~TextServerGDNative() {
- if (interface != nullptr) {
- interface->destructor(data);
- data = nullptr;
- interface = nullptr;
- }
-}
-
-/*************************************************************************/
-/* GDNative functions */
-/*************************************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static_assert(sizeof(godot_glyph) == sizeof(TextServer::Glyph), "Glyph size mismatch");
-static_assert(sizeof(godot_packed_glyph_array) == sizeof(Vector<TextServer::Glyph>), "Vector<Glyph> size mismatch");
-
-void GDAPI godot_text_register_interface(const godot_text_interface_gdnative *p_interface, const godot_string *p_name, uint32_t p_features) {
- ERR_FAIL_COND(p_interface->version.major != 1);
- String name = *(String *)p_name;
- TextServerManager::register_create_function(name + "(GDNative)", p_features, TextServerGDNative::create_func, (void *)p_interface);
-}
-
-// Glyph
-
-void GDAPI godot_glyph_new(godot_glyph *r_dest) {
- TextServer::Glyph *dest = (TextServer::Glyph *)r_dest;
- *dest = TextServer::Glyph();
-}
-
-godot_vector2i GDAPI godot_glyph_get_range(const godot_glyph *p_self) {
- godot_vector2i dest;
- Vector2i *d = (Vector2i *)&dest;
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- d->x = self->start;
- d->y = self->end;
- return dest;
-}
-
-void GDAPI godot_glyph_set_range(godot_glyph *p_self, const godot_vector2i *p_range) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- const Vector2i *range = (const Vector2i *)p_range;
- self->start = range->x;
- self->end = range->y;
-}
-
-godot_int GDAPI godot_glyph_get_count(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->count;
-}
-
-void GDAPI godot_glyph_set_count(godot_glyph *p_self, godot_int p_count) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->count = p_count;
-}
-
-godot_int GDAPI godot_glyph_get_repeat(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->repeat;
-}
-
-void GDAPI godot_glyph_set_repeat(godot_glyph *p_self, godot_int p_repeat) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->repeat = p_repeat;
-}
-
-godot_int GDAPI godot_glyph_get_flags(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->flags;
-}
-
-void GDAPI godot_glyph_set_flags(godot_glyph *p_self, godot_int p_flags) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->flags = p_flags;
-}
-
-godot_vector2 GDAPI godot_glyph_get_offset(const godot_glyph *p_self) {
- godot_vector2 dest;
- Vector2 *d = (Vector2 *)&dest;
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- d->x = self->x_off;
- d->y = self->y_off;
- return dest;
-}
-
-void GDAPI godot_glyph_set_offset(godot_glyph *p_self, const godot_vector2 *p_offset) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- const Vector2 *offset = (const Vector2 *)p_offset;
- self->x_off = offset->x;
- self->y_off = offset->y;
-}
-
-godot_real_t GDAPI godot_glyph_get_advance(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->advance;
-}
-
-void GDAPI godot_glyph_set_advance(godot_glyph *p_self, godot_real_t p_advance) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->advance = p_advance;
-}
-
-godot_rid GDAPI godot_glyph_get_font(const godot_glyph *p_self) {
- godot_rid dest;
- RID *d = (RID *)&dest;
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- *d = self->font_rid;
- return dest;
-}
-
-void GDAPI godot_glyph_set_font(godot_glyph *p_self, godot_rid *p_font) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- const RID *font = (const RID *)p_font;
- self->font_rid = *font;
-}
-
-godot_int GDAPI godot_glyph_get_font_size(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->font_size;
-}
-
-void GDAPI godot_glyph_set_font_size(godot_glyph *p_self, godot_int p_size) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->font_size = p_size;
-}
-
-godot_int GDAPI godot_glyph_get_index(const godot_glyph *p_self) {
- const TextServer::Glyph *self = (const TextServer::Glyph *)p_self;
- return self->index;
-}
-
-void GDAPI godot_glyph_set_index(godot_glyph *p_self, godot_int p_index) {
- TextServer::Glyph *self = (TextServer::Glyph *)p_self;
- self->index = p_index;
-}
-
-// GlyphArray
-
-void GDAPI godot_packed_glyph_array_new(godot_packed_glyph_array *r_dest) {
- Vector<TextServer::Glyph> *dest = (Vector<TextServer::Glyph> *)r_dest;
- memnew_placement(dest, Vector<TextServer::Glyph>);
-}
-
-void GDAPI godot_packed_glyph_array_new_copy(godot_packed_glyph_array *r_dest, const godot_packed_glyph_array *p_src) {
- Vector<TextServer::Glyph> *dest = (Vector<TextServer::Glyph> *)r_dest;
- const Vector<TextServer::Glyph> *src = (const Vector<TextServer::Glyph> *)p_src;
- memnew_placement(dest, Vector<TextServer::Glyph>(*src));
-}
-
-const godot_glyph GDAPI *godot_packed_glyph_array_ptr(const godot_packed_glyph_array *p_self) {
- const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
- return (const godot_glyph *)self->ptr();
-}
-
-godot_glyph GDAPI *godot_packed_glyph_array_ptrw(godot_packed_glyph_array *p_self) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- return (godot_glyph *)self->ptrw();
-}
-
-void GDAPI godot_packed_glyph_array_append(godot_packed_glyph_array *p_self, const godot_glyph *p_data) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_glyph_array_append_array(godot_packed_glyph_array *p_self, const godot_packed_glyph_array *p_array) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- Vector<TextServer::Glyph> *array = (Vector<TextServer::Glyph> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_glyph_array_insert(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
- return (godot_error)self->insert(p_idx, s);
-}
-
-godot_bool GDAPI godot_packed_glyph_array_has(godot_packed_glyph_array *p_self, const godot_glyph *p_value) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- TextServer::Glyph &v = *(TextServer::Glyph *)p_value;
- return (godot_bool)self->has(v);
-}
-
-void GDAPI godot_packed_glyph_array_sort(godot_packed_glyph_array *p_self) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_glyph_array_reverse(godot_packed_glyph_array *p_self) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- self->reverse();
-}
-
-void GDAPI godot_packed_glyph_array_push_back(godot_packed_glyph_array *p_self, const godot_glyph *p_data) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_glyph_array_remove(godot_packed_glyph_array *p_self, const godot_int p_idx) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_glyph_array_resize(godot_packed_glyph_array *p_self, const godot_int p_size) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_glyph_array_set(godot_packed_glyph_array *p_self, const godot_int p_idx, const godot_glyph *p_data) {
- Vector<TextServer::Glyph> *self = (Vector<TextServer::Glyph> *)p_self;
- TextServer::Glyph &s = *(TextServer::Glyph *)p_data;
- self->set(p_idx, s);
-}
-
-godot_glyph GDAPI godot_packed_glyph_array_get(const godot_packed_glyph_array *p_self, const godot_int p_idx) {
- const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
- godot_glyph v;
- TextServer::Glyph *s = (TextServer::Glyph *)&v;
- *s = self->get(p_idx);
- return v;
-}
-
-godot_int GDAPI godot_packed_glyph_array_size(const godot_packed_glyph_array *p_self) {
- const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
- return self->size();
-}
-
-godot_bool GDAPI godot_packed_glyph_array_is_empty(const godot_packed_glyph_array *p_self) {
- const Vector<TextServer::Glyph> *self = (const Vector<TextServer::Glyph> *)p_self;
- return self->is_empty();
-}
-
-void GDAPI godot_packed_glyph_array_destroy(godot_packed_glyph_array *p_self) {
- ((Vector<TextServer::Glyph> *)p_self)->~Vector();
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/modules/gdnative/text/text_server_gdnative.h b/modules/gdnative/text/text_server_gdnative.h
deleted file mode 100644
index f081637e23..0000000000
--- a/modules/gdnative/text/text_server_gdnative.h
+++ /dev/null
@@ -1,254 +0,0 @@
-/*************************************************************************/
-/* text_server_gdnative.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef TEXT_SERVER_GDNATIVE_H
-#define TEXT_SERVER_GDNATIVE_H
-
-#include "modules/gdnative/gdnative.h"
-
-#include "servers/text_server.h"
-
-class TextServerGDNative : public TextServer {
- GDCLASS(TextServerGDNative, TextServer);
-
- const godot_text_interface_gdnative *interface = nullptr;
- void *data = nullptr;
-
-protected:
- static void _bind_methods(){};
-
-public:
- virtual bool has_feature(Feature p_feature) override;
- virtual String get_name() const override;
-
- virtual void free(RID p_rid) override;
- virtual bool has(RID p_rid) override;
- virtual bool load_support_data(const String &p_filename) override;
-
-#ifdef TOOLS_ENABLED
- virtual String get_support_data_filename() override;
- virtual String get_support_data_info() override;
- virtual bool save_support_data(const String &p_filename) override;
-#endif
-
- virtual bool is_locale_right_to_left(const String &p_locale) override;
-
- virtual int32_t name_to_tag(const String &p_name) const override;
- virtual String tag_to_name(int32_t p_tag) const override;
-
- /* Font interface */
- virtual RID create_font() override;
-
- virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
- virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
-
- virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
- virtual bool font_is_antialiased(RID p_font_rid) const override;
-
- virtual void font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) override;
- virtual bool font_is_multichannel_signed_distance_field(RID p_font_rid) const override;
-
- virtual void font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) override;
- virtual int font_get_msdf_pixel_range(RID p_font_rid) const override;
-
- virtual void font_set_msdf_size(RID p_font_rid, int p_msdf_size) override;
- virtual int font_get_msdf_size(RID p_font_rid) const override;
-
- virtual void font_set_fixed_size(RID p_font_rid, int p_fixed_size) override;
- virtual int font_get_fixed_size(RID p_font_rid) const override;
-
- virtual void font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) override;
- virtual bool font_is_force_autohinter(RID p_font_rid) const override;
-
- virtual void font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) override;
- virtual TextServer::Hinting font_get_hinting(RID p_font_rid) const override;
-
- virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
- virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
-
- virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
- virtual real_t font_get_oversampling(RID p_font_rid) const override;
-
- virtual Array font_get_size_cache_list(RID p_font_rid) const override;
- virtual void font_clear_size_cache(RID p_font_rid) override;
- virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override;
-
- virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
- virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
-
- virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
- virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
-
- virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
- virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
-
- virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
- virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
-
- virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
- virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
-
- virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
- virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
-
- virtual int font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const override;
- virtual void font_clear_textures(RID p_font_rid, const Vector2i &p_size) override;
- virtual void font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) override;
-
- virtual void font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) override;
- virtual Ref<Image> font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
-
- virtual void font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) override;
- virtual PackedInt32Array font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const override;
-
- virtual Array font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const override;
- virtual void font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) override;
- virtual void font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) override;
-
- virtual Vector2 font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const override;
- virtual void font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) override;
-
- virtual Vector2 font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
- virtual void font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) override;
-
- virtual Vector2 font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
- virtual void font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) override;
-
- virtual Rect2 font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
- virtual void font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) override;
-
- virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
- virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
-
- virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
-
- virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
- virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
- virtual void font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) override;
-
- virtual void font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) override;
- virtual Vector2 font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const override;
-
- virtual int32_t font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector = 0) const override;
-
- virtual bool font_has_char(RID p_font_rid, char32_t p_char) const override;
- virtual String font_get_supported_chars(RID p_font_rid) const override;
-
- virtual void font_render_range(RID p_font, const Vector2i &p_size, char32_t p_start, char32_t p_end) override;
- virtual void font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) override;
-
- virtual void font_draw_glyph(RID p_font, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- virtual void font_draw_glyph_outline(RID p_font, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
-
- virtual bool font_is_language_supported(RID p_font_rid, const String &p_language) const override;
- virtual void font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) override;
- virtual bool font_get_language_support_override(RID p_font_rid, const String &p_language) override;
- virtual void font_remove_language_support_override(RID p_font_rid, const String &p_language) override;
- virtual Vector<String> font_get_language_support_overrides(RID p_font_rid) override;
-
- virtual bool font_is_script_supported(RID p_font_rid, const String &p_script) const override;
- virtual void font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) override;
- virtual bool font_get_script_support_override(RID p_font_rid, const String &p_script) override;
- virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
- virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
-
- virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
- virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
-
- virtual real_t font_get_global_oversampling() const override;
- virtual void font_set_global_oversampling(real_t p_oversampling) override;
-
- /* Shaped text buffer interface */
-
- virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
-
- virtual void shaped_text_clear(RID p_shaped) override;
-
- virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
- virtual Direction shaped_text_get_direction(RID p_shaped) const override;
-
- virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
-
- virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
- virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
-
- virtual void shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) override;
- virtual bool shaped_text_get_preserve_invalid(RID p_shaped) const override;
-
- virtual void shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) override;
- virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
-
- virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
- virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override;
- virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override;
-
- virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
- virtual RID shaped_text_get_parent(RID p_shaped) const override;
-
- virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
-
- virtual bool shaped_text_shape(RID p_shaped) override;
- virtual bool shaped_text_update_breaks(RID p_shaped) override;
- virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
-
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
-
- virtual bool shaped_text_is_ready(RID p_shaped) const override;
-
- virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
-
- virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
-
- virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
- virtual Vector<Vector2i> shaped_text_get_line_breaks_adv(RID p_shaped, const Vector<real_t> &p_width, int p_start = 0, bool p_once = true, uint8_t /*TextBreakFlag*/ p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
- virtual Vector<Vector2i> shaped_text_get_line_breaks(RID p_shaped, real_t p_width, int p_start = 0, uint8_t p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
- virtual Vector<Vector2i> shaped_text_get_word_breaks(RID p_shaped, int p_grapheme_flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_PUNCTUATION) const override;
- virtual Array shaped_text_get_objects(RID p_shaped) const override;
- virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
-
- virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
- virtual real_t shaped_text_get_descent(RID p_shaped) const override;
- virtual real_t shaped_text_get_width(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
-
- virtual String format_number(const String &p_string, const String &p_language = "") const override;
- virtual String parse_number(const String &p_string, const String &p_language = "") const override;
- virtual String percent_sign(const String &p_language = "") const override;
-
- static TextServer *create_func(Error &r_error, void *p_user_data);
-
- TextServerGDNative();
- ~TextServerGDNative();
-};
-
-#endif // TEXT_SERVER_GDNATIVE_H
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index 4c9bfb395d..e249363016 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -341,7 +341,7 @@ void VideoStreamGDNative::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamGDNative::set_file);
ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamGDNative::get_file);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
}
void VideoStreamGDNative::set_audio_track(int p_track) {
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 51b3452a3a..33f4198ac1 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -4,10 +4,10 @@
Built-in GDScript functions.
</brief_description>
<description>
- List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.)
+ A list of GDScript-specific utility functions accessed in any script.
+ For the list of the global functions and constants see [@GlobalScope].
</description>
<tutorials>
- <link title="Random number generation">https://docs.godotengine.org/en/latest/tutorials/math/random_number_generation.html</link>
</tutorials>
<methods>
<method name="Color8">
@@ -98,6 +98,7 @@
[codeblock]
[{function:bar, line:12, source:res://script.gd}, {function:foo, line:9, source:res://script.gd}, {function:_ready, line:6, source:res://script.gd}]
[/codeblock]
+ [b]Note:[/b] Not supported for calling from threads. Instead, this will return an empty array.
</description>
</method>
<method name="inst2dict">
@@ -160,23 +161,31 @@
<method name="print_debug" qualifiers="vararg">
<return type="void" />
<description>
- Like [method @GlobalScope.print], but prints only when used in debug mode.
+ Like [method @GlobalScope.print], but includes the current stack frame when running with the debugger turned on.
+ Output in the console would look something like this:
+ [codeblock]
+ Test print
+ At: res://test.gd:15:_process()
+ [/codeblock]
+ [b]Note:[/b] Not supported for calling from threads. Instead of the stack frame, this will print the thread ID.
</description>
</method>
<method name="print_stack">
<return type="void" />
<description>
- Prints a stack track at code location, only works when running with debugger turned on.
+ Prints a stack trace at the current code location. Only works when running with debugger turned on.
Output in the console would look something like this:
[codeblock]
Frame 0 - res://test.gd:16 in function '_process'
[/codeblock]
+ [b]Note:[/b] Not supported for calling from threads. Instead of the stack trace, this will print the thread ID.
</description>
</method>
<method name="range" qualifiers="vararg">
<return type="Array" />
<description>
- Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment).
+ Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
+ Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run.
[codeblock]
print(range(4))
print(range(2, 5))
@@ -188,6 +197,20 @@
[2, 3, 4]
[0, 2, 4]
[/codeblock]
+ To iterate over an [Array] backwards, use:
+ [codeblock]
+ var array = [3, 6, 9]
+ var i := array.size() - 1
+ while i &gt;= 0:
+ print(array[i])
+ i -= 1
+ [/codeblock]
+ Output:
+ [codeblock]
+ 9
+ 6
+ 3
+ [/codeblock]
</description>
</method>
<method name="str" qualifiers="vararg">
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 72738f027a..5acb29e748 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
+ <link title="GDScript documentation index">$DOCS_URL/tutorials/scripting/gdscript/index.html</link>
</tutorials>
<methods>
<method name="get_as_byte_code" qualifiers="const">
@@ -30,6 +30,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ab441d194a..4f711dfd1e 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -413,7 +413,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
previous_column = j;
// ignore if just whitespace
- if (text != "") {
+ if (!text.is_empty()) {
previous_text = text;
}
}
@@ -467,7 +467,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
for (const StringName &E : global_classes) {
- keywords[String(E)] = usertype_color;
+ keywords[E] = usertype_color;
}
/* Autoloads. */
@@ -486,7 +486,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> core_types;
gdscript->get_core_type_words(&core_types);
for (const String &E : core_types) {
- keywords[E] = basetype_color;
+ keywords[StringName(E)] = basetype_color;
}
/* Reserved words. */
@@ -496,9 +496,9 @@ void GDScriptSyntaxHighlighter::_update_cache() {
gdscript->get_reserved_words(&keyword_list);
for (const String &E : keyword_list) {
if (gdscript->is_control_flow_keyword(E)) {
- keywords[E] = control_flow_keyword_color;
+ keywords[StringName(E)] = control_flow_keyword_color;
} else {
- keywords[E] = keyword_color;
+ keywords[StringName(E)] = keyword_color;
}
}
@@ -509,7 +509,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
- add_color_region(beg, end, comment_color, end == "");
+ add_color_region(beg, end, comment_color, end.is_empty());
}
/* Strings */
@@ -519,7 +519,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
for (const String &string : strings) {
String beg = string.get_slice(" ", 0);
String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
- add_color_region(beg, end, string_color, end == "");
+ add_color_region(beg, end, string_color, end.is_empty());
}
const Ref<Script> script = _get_edited_resource();
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index fabd64dab8..07f21b34ae 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -47,8 +47,8 @@ private:
Vector<ColorRegion> color_regions;
Map<int, int> color_region_cache;
- Dictionary keywords;
- Dictionary member_keywords;
+ HashMap<StringName, Color> keywords;
+ HashMap<StringName, Color> member_keywords;
enum Type {
NONE,
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 4589cf1a36..4822e411ce 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -87,6 +87,19 @@ Object *GDScriptNativeClass::instantiate() {
return ClassDB::instantiate(name);
}
+GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) {
+ if (p_script->initializer) {
+ return p_script->initializer;
+ } else {
+ GDScript *base = p_script->_base;
+ if (base != nullptr) {
+ return _super_constructor(base);
+ } else {
+ return nullptr;
+ }
+ }
+}
+
void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) {
GDScript *base = p_script->_base;
if (base != nullptr) {
@@ -109,8 +122,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
instance->owner_id = p_owner->get_instance_id();
#ifdef DEBUG_ENABLED
//needed for hot reloading
- for (Map<StringName, MemberInfo>::Element *E = member_indices.front(); E; E = E->next()) {
- instance->member_indices_cache[E->key()] = E->get().index;
+ for (const KeyValue<StringName, MemberInfo> &E : member_indices) {
+ instance->member_indices_cache[E.key] = E.value.index;
}
#endif
instance->owner->set_script_instance(instance);
@@ -135,6 +148,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
if (p_argcount < 0) {
return instance;
}
+
+ initializer = _super_constructor(this);
if (initializer != nullptr) {
initializer->call(instance, p_args, p_argcount, r_error);
if (r_error.error != Callable::CallError::CALL_OK) {
@@ -238,10 +253,10 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const {
const GDScript *current = this;
while (current) {
- for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) {
- GDScriptFunction *func = E->get();
+ for (const KeyValue<StringName, GDScriptFunction *> &E : current->member_functions) {
+ GDScriptFunction *func = E.value;
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arginfo = func->get_argument_type(i);
#ifdef TOOLS_ENABLED
@@ -271,11 +286,11 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
while (sptr) {
Vector<_GDScriptMemberSort> msort;
- for (Map<StringName, PropertyInfo>::Element *E = sptr->member_info.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, PropertyInfo> &E : sptr->member_info) {
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(E->key()));
- ms.index = sptr->member_indices[E->key()].index;
- ms.name = E->key();
+ ERR_CONTINUE(!sptr->member_indices.has(E.key));
+ ms.index = sptr->member_indices[E.key].index;
+ ms.name = E.key;
msort.push_back(ms);
}
@@ -346,7 +361,7 @@ ScriptInstance *GDScript::instance_create(Object *p_this) {
if (top->native.is_valid()) {
if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
+ GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
}
ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instantiated in object of type '" + p_this->get_class() + "'" + ".");
}
@@ -374,7 +389,7 @@ bool GDScript::instance_has(const Object *p_this) const {
}
bool GDScript::has_source_code() const {
- return source != "";
+ return !source.is_empty();
}
String GDScript::get_source_code() const {
@@ -397,8 +412,8 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro
base_cache->_update_exports_values(values, propnames);
}
- for (Map<StringName, Variant>::Element *E = member_default_values_cache.front(); E; E = E->next()) {
- values[E->key()] = E->get();
+ for (const KeyValue<StringName, Variant> &E : member_default_values_cache) {
+ values[E.key] = E.value;
}
for (const PropertyInfo &E : members_cache) {
@@ -412,7 +427,7 @@ void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
} else {
for (int i = 0; i < docs.size(); i++) {
if (docs[i].name == p_inner_class.name) {
- docs.remove(i);
+ docs.remove_at(i);
break;
}
}
@@ -443,7 +458,7 @@ void GDScript::_update_doc() {
doc.is_script_doc = true;
if (base.is_valid() && base->is_valid()) {
- if (base->doc.name != String()) {
+ if (!base->doc.name.is_empty()) {
doc.inherits = base->doc.name;
} else {
doc.inherits = base->get_instance_base_type();
@@ -456,9 +471,9 @@ void GDScript::_update_doc() {
doc.description = doc_description;
doc.tutorials = doc_tutorials;
- for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) {
- if (E->value().description != "") {
- doc.enums[E->key()] = E->value().description;
+ for (const KeyValue<String, DocData::EnumDoc> &E : doc_enums) {
+ if (!E.value.description.is_empty()) {
+ doc.enums[E.key] = E.value.description;
}
}
@@ -530,36 +545,36 @@ void GDScript::_update_doc() {
for (int i = 0; i < signals.size(); i++) {
DocData::MethodDoc signal_doc;
if (doc_signals.has(signals[i].name)) {
- DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name);
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], doc_signals[signals[i].name]);
} else {
DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
}
doc.signals.push_back(signal_doc);
}
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- if (subclasses.has(E->key())) {
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ if (subclasses.has(E.key)) {
continue;
}
// Enums.
bool is_enum = false;
- if (E->value().get_type() == Variant::DICTIONARY) {
- if (doc_enums.has(E->key())) {
+ if (E.value.get_type() == Variant::DICTIONARY) {
+ if (doc_enums.has(E.key)) {
is_enum = true;
- for (int i = 0; i < doc_enums[E->key()].values.size(); i++) {
- doc_enums[E->key()].values.write[i].enumeration = E->key();
- doc.constants.push_back(doc_enums[E->key()].values[i]);
+ for (int i = 0; i < doc_enums[E.key].values.size(); i++) {
+ doc_enums[E.key].values.write[i].enumeration = E.key;
+ doc.constants.push_back(doc_enums[E.key].values[i]);
}
}
}
if (!is_enum && doc_enums.has("@unnamed_enums")) {
for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) {
- if (E->key() == doc_enums["@unnamed_enums"].values[i].name) {
+ if (E.key == doc_enums["@unnamed_enums"].values[i].name) {
is_enum = true;
DocData::ConstantDoc constant_doc;
constant_doc.enumeration = "@unnamed_enums";
- DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description);
+ DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_enums["@unnamed_enums"].values[i].description);
doc.constants.push_back(constant_doc);
break;
}
@@ -568,16 +583,16 @@ void GDScript::_update_doc() {
if (!is_enum) {
DocData::ConstantDoc constant_doc;
String doc_description;
- if (doc_constants.has(E->key())) {
- doc_description = doc_constants[E->key()];
+ if (doc_constants.has(E.key)) {
+ doc_description = doc_constants[E.key];
}
- DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description);
+ DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_description);
doc.constants.push_back(constant_doc);
}
}
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- E->get()->_update_doc();
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_update_doc();
}
_add_doc(doc);
@@ -601,11 +616,11 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
String basedir = path;
- if (basedir == "") {
+ if (basedir.is_empty()) {
basedir = get_path();
}
- if (basedir != "") {
+ if (!basedir.is_empty()) {
basedir = basedir.get_base_dir();
}
@@ -625,9 +640,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
String path = "";
if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) {
path = c->extends_path;
- if (path.is_rel_path()) {
+ if (path.is_relative_path()) {
String base = get_path();
- if (base == "" || base.is_rel_path()) {
+ if (base.is_empty() || base.is_relative_path()) {
ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data());
} else {
path = base.get_base_dir().plus_file(path);
@@ -641,7 +656,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
}
}
- if (path != "") {
+ if (!path.is_empty()) {
if (path != get_path()) {
Ref<GDScript> bf = ResourceLoader::load(path);
@@ -769,8 +784,16 @@ void GDScript::update_exports() {
void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) {
p_sc->path = p_path;
- for (Map<StringName, Ref<GDScript>>::Element *E = p_sc->subclasses.front(); E; E = E->next()) {
- _set_subclass_path(E->get(), p_path);
+ for (KeyValue<StringName, Ref<GDScript>> &E : p_sc->subclasses) {
+ _set_subclass_path(E.value, p_path);
+ }
+}
+
+String GDScript::_get_debug_path() const {
+ if (is_built_in() && !get_name().is_empty()) {
+ return get_name() + " (" + get_path().get_slice("::", 0) + ")";
+ } else {
+ return get_path();
}
}
@@ -786,11 +809,11 @@ Error GDScript::reload(bool p_keep_state) {
String basedir = path;
- if (basedir == "") {
+ if (basedir.is_empty()) {
basedir = get_path();
}
- if (basedir != "") {
+ if (!basedir.is_empty()) {
basedir = basedir.get_base_dir();
}
@@ -817,10 +840,10 @@ Error GDScript::reload(bool p_keep_state) {
Error err = parser.parse(source, path, false);
if (err) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
+ GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
// TODO: Show all error messages.
- _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
ERR_FAIL_V(ERR_PARSE_ERROR);
}
@@ -829,12 +852,12 @@ Error GDScript::reload(bool p_keep_state) {
if (err) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
+ GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message);
}
const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front();
while (e != nullptr) {
- _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
e = e->next();
}
ERR_FAIL_V(ERR_PARSE_ERROR);
@@ -852,9 +875,9 @@ Error GDScript::reload(bool p_keep_state) {
if (err) {
if (can_run) {
if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
+ GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
- _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
ERR_FAIL_V(ERR_COMPILATION_FAILED);
} else {
return err;
@@ -864,15 +887,15 @@ Error GDScript::reload(bool p_keep_state) {
for (const GDScriptWarning &warning : parser.get_warnings()) {
if (EngineDebugger::is_active()) {
Vector<ScriptLanguage::StackInfo> si;
- EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si);
+ EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si);
}
}
#endif
valid = true;
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- _set_subclass_path(E->get(), path);
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ _set_subclass_path(E.value, path);
}
_init_rpc_methods_properties();
@@ -886,8 +909,8 @@ ScriptLanguage *GDScript::get_language() const {
void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
if (p_constants) {
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- (*p_constants)[E->key()] = E->value();
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ (*p_constants)[E.key] = E.value;
}
}
}
@@ -900,7 +923,7 @@ void GDScript::get_members(Set<StringName> *p_members) {
}
}
-const Vector<MultiplayerAPI::RPCConfig> GDScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const {
return rpc_functions;
}
@@ -964,7 +987,7 @@ bool GDScript::_set(const StringName &p_name, const Variant &p_value) {
}
void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
- p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void GDScript::_bind_methods() {
@@ -1017,9 +1040,9 @@ const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions(
}
StringName GDScript::debug_get_member_by_index(int p_idx) const {
- for (const Map<StringName, MemberInfo>::Element *E = member_indices.front(); E; E = E->next()) {
- if (E->get().index == p_idx) {
- return E->key();
+ for (const KeyValue<StringName, MemberInfo> &E : member_indices) {
+ if (E.value.index == p_idx) {
+ return E.key;
}
}
@@ -1064,12 +1087,12 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
}
void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
- for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<StringName>> &E : _signals) {
MethodInfo mi;
- mi.name = E->key();
- for (int i = 0; i < E->get().size(); i++) {
+ mi.name = E.key;
+ for (int i = 0; i < E.value.size(); i++) {
PropertyInfo arg;
- arg.name = E->get()[i];
+ arg.name = E.value[i];
mi.arguments.push_back(arg);
}
r_list->push_back(mi);
@@ -1099,7 +1122,7 @@ String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript)
String class_name;
while (p_gdscript) {
- if (class_name == "") {
+ if (class_name.is_empty()) {
class_name = p_gdscript->get_script_class_name();
} else {
class_name = p_gdscript->get_script_class_name() + "." + class_name;
@@ -1127,11 +1150,11 @@ void GDScript::_save_orphaned_subclasses() {
};
Vector<ClassRefWithName> weak_subclasses;
// collect subclasses ObjectID and name
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- E->get()->_owner = nullptr; //bye, you are no longer owned cause I died
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_owner = nullptr; //bye, you are no longer owned cause I died
ClassRefWithName subclass;
- subclass.id = E->get()->get_instance_id();
- subclass.fully_qualified_name = E->get()->fully_qualified_name;
+ subclass.id = E.value->get_instance_id();
+ subclass.fully_qualified_name = E.value->fully_qualified_name;
weak_subclasses.push_back(subclass);
}
@@ -1163,10 +1186,10 @@ void GDScript::_init_rpc_methods_properties() {
Map<StringName, Ref<GDScript>>::Element *sub_E = subclasses.front();
while (cscript) {
// RPC Methods
- for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) {
- MultiplayerAPI::RPCConfig config = E->get()->get_rpc_config();
- if (config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
- config.name = E->get()->get_name();
+ for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
+ Multiplayer::RPCConfig config = E.value->get_rpc_config();
+ if (config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
+ config.name = E.value->get_name();
if (rpc_functions.find(config) == -1) {
rpc_functions.push_back(config);
}
@@ -1185,7 +1208,7 @@ void GDScript::_init_rpc_methods_properties() {
}
// Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
+ rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
GDScript::~GDScript() {
@@ -1200,8 +1223,8 @@ GDScript::~GDScript() {
}
}
- for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ memdelete(E.value);
}
if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
@@ -1242,6 +1265,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
call(member->setter, &val, 1, err);
if (err.error == Callable::CallError::CALL_OK) {
return true; //function exists, call was successful
+ } else {
+ return false;
}
} else {
if (member->data_type.has_type) {
@@ -1408,7 +1433,7 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
pinfo.type = Variant::Type(d["type"].operator int());
ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX);
pinfo.name = d["name"];
- ERR_CONTINUE(pinfo.name == "");
+ ERR_CONTINUE(pinfo.name.is_empty());
if (d.has("hint")) {
pinfo.hint = PropertyHint(d["hint"].operator int());
}
@@ -1427,11 +1452,11 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
//instance a fake script for editing the values
Vector<_GDScriptMemberSort> msort;
- for (Map<StringName, PropertyInfo>::Element *F = sptr->member_info.front(); F; F = F->next()) {
+ for (const KeyValue<StringName, PropertyInfo> &F : sptr->member_info) {
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(F->key()));
- ms.index = sptr->member_indices[F->key()].index;
- ms.name = F->key();
+ ERR_CONTINUE(!sptr->member_indices.has(F.key));
+ ms.index = sptr->member_indices[F.key].index;
+ ms.name = F.key;
msort.push_back(ms);
}
@@ -1452,11 +1477,11 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
const GDScript *sptr = script.ptr();
while (sptr) {
- for (Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
mi.flags |= METHOD_FLAG_FROM_SCRIPT;
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
+ for (int i = 0; i < E.value->get_argument_count(); i++) {
mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
}
p_list->push_back(mi);
@@ -1541,7 +1566,7 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
-const Vector<MultiplayerAPI::RPCConfig> GDScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> GDScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
@@ -1554,10 +1579,10 @@ void GDScriptInstance::reload_members() {
new_members.resize(script->member_indices.size());
//pass the values to the new indices
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- if (member_indices_cache.has(E->key())) {
- Variant value = members[member_indices_cache[E->key()]];
- new_members.write[E->get().index] = value;
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ if (member_indices_cache.has(E.key)) {
+ Variant value = members[member_indices_cache[E.key]];
+ new_members.write[E.value.index] = value;
}
}
@@ -1566,8 +1591,8 @@ void GDScriptInstance::reload_members() {
//pass the values to the new indices
member_indices_cache.clear();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- member_indices_cache[E->key()] = E->get().index;
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ member_indices_cache[E.key] = E.value.index;
}
#endif
@@ -1875,21 +1900,21 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#endif
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = script->pending_reload_state.front(); F; F = F->next()) {
- map[F->key()] = F->get(); //pending to reload, use this one instead
+ for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : script->pending_reload_state) {
+ map[F.key] = F.value; //pending to reload, use this one instead
}
}
}
- for (Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<GDScript> scr = E->key();
+ for (KeyValue<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
+ Ref<GDScript> scr = E.key;
scr->reload(p_soft_reload);
//restore state if saved
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = E->get().front(); F; F = F->next()) {
- List<Pair<StringName, Variant>> &saved_state = F->get();
+ for (KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : E.value) {
+ List<Pair<StringName, Variant>> &saved_state = F.value;
- Object *obj = ObjectDB::get_instance(F->key());
+ Object *obj = ObjectDB::get_instance(F.key);
if (!obj) {
continue;
}
@@ -2025,15 +2050,15 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const {
return p_keyword == "break" ||
- p_keyword == "continue" ||
- p_keyword == "elif" ||
- p_keyword == "else" ||
- p_keyword == "if" ||
- p_keyword == "for" ||
- p_keyword == "match" ||
- p_keyword == "pass" ||
- p_keyword == "return" ||
- p_keyword == "while";
+ p_keyword == "continue" ||
+ p_keyword == "elif" ||
+ p_keyword == "else" ||
+ p_keyword == "if" ||
+ p_keyword == "for" ||
+ p_keyword == "match" ||
+ p_keyword == "pass" ||
+ p_keyword == "return" ||
+ p_keyword == "while";
}
bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
@@ -2059,7 +2084,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (r_icon_path) {
if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
*r_icon_path = c->icon_path;
- } else if (c->icon_path.is_rel_path()) {
+ } else if (c->icon_path.is_relative_path()) {
*r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path();
}
}
@@ -2087,7 +2112,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
break;
}
String subpath = subclass->extends_path;
- if (subpath.is_rel_path()) {
+ if (subpath.is_relative_path()) {
subpath = path.get_base_dir().plus_file(subpath).simplify_path();
}
@@ -2106,7 +2131,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class;
if (inner_class->identifier->name == extend_classes[0]) {
- extend_classes.remove(0);
+ extend_classes.remove_at(0);
found = true;
subclass = inner_class;
break;
@@ -2195,15 +2220,15 @@ GDScriptLanguage::~GDScriptLanguage() {
// is not the same as before).
script->reference();
- for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) {
- GDScriptFunction *func = E->get();
+ for (KeyValue<StringName, GDScriptFunction *> &E : script->member_functions) {
+ GDScriptFunction *func = E.value;
for (int i = 0; i < func->argument_types.size(); i++) {
func->argument_types.write[i].script_type_ref = Ref<Script>();
}
func->return_type.script_type_ref = Ref<Script>();
}
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- E->get().data_type.script_type_ref = Ref<Script>();
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
}
s = s->next();
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 24809ad5fd..ade4f247c9 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -85,7 +85,7 @@ class GDScript : public Script {
Map<StringName, MemberInfo> member_indices; //members are just indices to the instantiated script.
Map<StringName, Ref<GDScript>> subclasses;
Map<StringName, Vector<StringName>> _signals;
- Vector<MultiplayerAPI::RPCConfig> rpc_functions;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
@@ -130,10 +130,12 @@ class GDScript : public Script {
SelfList<GDScriptFunctionState>::List pending_func_states;
+ GDScriptFunction *_super_constructor(GDScript *p_script);
void _super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error);
GDScriptInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
void _set_subclass_path(Ref<GDScript> &p_sc, const String &p_path);
+ String _get_debug_path() const;
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
@@ -245,7 +247,7 @@ public:
virtual void get_constants(Map<StringName, Variant> *p_constants) override;
virtual void get_members(Set<StringName> *p_members) override;
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@@ -298,7 +300,7 @@ public:
void reload_members();
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
GDScriptInstance();
~GDScriptInstance();
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 6b7403d854..7b64c0564e 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -132,6 +132,81 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
return type;
}
+bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class) {
+ if (p_class->members_indices.has(p_member_name)) {
+ int index = p_class->members_indices[p_member_name];
+ const GDScriptParser::ClassNode::Member *member = &p_class->members[index];
+
+ if (member->type == GDScriptParser::ClassNode::Member::VARIABLE ||
+ member->type == GDScriptParser::ClassNode::Member::CONSTANT ||
+ member->type == GDScriptParser::ClassNode::Member::ENUM ||
+ member->type == GDScriptParser::ClassNode::Member::ENUM_VALUE ||
+ member->type == GDScriptParser::ClassNode::Member::CLASS ||
+ member->type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool GDScriptAnalyzer::has_member_name_conflict_in_native_type(const StringName &p_member_name, const StringName &p_native_type_string) {
+ if (ClassDB::has_signal(p_native_type_string, p_member_name)) {
+ return true;
+ }
+ if (ClassDB::has_property(p_native_type_string, p_member_name)) {
+ return true;
+ }
+ if (ClassDB::has_integer_constant(p_native_type_string, p_member_name)) {
+ return true;
+ }
+
+ return false;
+}
+
+Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string) {
+ if (has_member_name_conflict_in_native_type(p_member_name, p_native_type_string)) {
+ push_error(vformat(R"(Member "%s" redefined (original in native class '%s'))", p_member_name, p_native_type_string), p_member_node);
+ return ERR_PARSE_ERROR;
+ }
+
+ if (class_exists(p_member_name)) {
+ push_error(vformat(R"(The member "%s" shadows a native class.)", p_member_name), p_member_node);
+ return ERR_PARSE_ERROR;
+ }
+
+ if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) {
+ push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
+Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) {
+ const GDScriptParser::DataType *current_data_type = &p_class_node->base_type;
+ while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
+ GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
+ if (has_member_name_conflict_in_script_class(p_member_name, current_class_node)) {
+ push_error(vformat(R"(The member "%s" already exists in a parent class.)", p_member_name),
+ p_member_node);
+ return ERR_PARSE_ERROR;
+ }
+ current_data_type = &current_class_node->base_type;
+ }
+
+ if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) {
+ if (current_data_type->native_type != StringName("")) {
+ return check_native_member_name_conflict(
+ p_member_name,
+ p_member_node,
+ current_data_type->native_type);
+ }
+ }
+
+ return OK;
+}
+
Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
if (p_class->base_type.is_set()) {
// Already resolved
@@ -148,6 +223,17 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name);
}
+ if (p_class->identifier) {
+ StringName class_name = p_class->identifier->name;
+ if (class_exists(class_name)) {
+ push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier);
+ } else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) {
+ push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier);
+ } else if (ProjectSettings::get_singleton()->has_autoload(class_name) && ProjectSettings::get_singleton()->get_autoload(class_name).is_singleton) {
+ push_error(vformat(R"(Class "%s" hides an autoload singleton.)", class_name), p_class->identifier);
+ }
+ }
+
GDScriptParser::DataType result;
// Set datatype for class.
@@ -158,6 +244,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.kind = GDScriptParser::DataType::CLASS;
class_type.class_type = p_class;
class_type.script_path = parser->script_path;
+ class_type.builtin_type = Variant::OBJECT;
p_class->set_datatype(class_type);
if (!p_class->extends_used) {
@@ -172,6 +259,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
int extends_index = 0;
if (!p_class->extends_path.is_empty()) {
+ if (p_class->extends_path.is_relative_path()) {
+ p_class->extends_path = class_type.script_path.get_base_dir().plus_file(p_class->extends_path).simplify_path();
+ }
Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
if (parser.is_null()) {
push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
@@ -380,6 +470,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+ container_type.is_meta_type = false;
+ container_type.is_constant = false;
result.set_container_element_type(container_type);
}
}
@@ -392,7 +484,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = parser->head->get_datatype();
} else {
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
- if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
return GDScriptParser::DataType();
}
@@ -525,6 +617,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
switch (member.type) {
case GDScriptParser::ClassNode::Member::VARIABLE: {
+ check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
+
GDScriptParser::DataType datatype;
datatype.kind = GDScriptParser::DataType::VARIANT;
datatype.type_source = GDScriptParser::DataType::UNDETECTED;
@@ -598,6 +692,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
} break;
case GDScriptParser::ClassNode::Member::CONSTANT: {
+ check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
+
reduce_expression(member.constant->initializer);
GDScriptParser::DataType specified_type;
@@ -607,8 +703,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
specified_type.is_meta_type = false;
}
- GDScriptParser::DataType datatype = member.constant->get_datatype();
+ GDScriptParser::DataType datatype;
if (member.constant->initializer) {
+ datatype = member.constant->initializer->get_datatype();
if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer);
const_fold_array(array);
@@ -647,6 +744,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
+ check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal);
+
for (int j = 0; j < member.signal->parameters.size(); j++) {
GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier);
signal_type.is_meta_type = false;
@@ -666,6 +765,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
+ check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
+
GDScriptParser::DataType enum_type;
enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
enum_type.kind = GDScriptParser::DataType::ENUM;
@@ -717,6 +818,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
if (member.enum_value.custom_value) {
+ check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value);
+
current_enum = member.enum_value.parent_enum;
reduce_expression(member.enum_value.custom_value);
current_enum = nullptr;
@@ -730,6 +833,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
member.enum_value.resolved = true;
}
} else {
+ check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum);
+
if (member.enum_value.index > 0) {
member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1;
} else {
@@ -742,7 +847,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
p_class->members.write[i].enum_value = member.enum_value;
} break;
case GDScriptParser::ClassNode::Member::CLASS:
- break; // Done later.
+ check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
+ break;
case GDScriptParser::ClassNode::Member::UNDEFINED:
ERR_PRINT("Trying to resolve undefined member.");
break;
@@ -771,18 +877,34 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
- // Do functions now.
+ // Do functions and properties now.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
- continue;
- }
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ resolve_function_body(member.function);
- resolve_function_body(member.function);
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
+ E->apply(parser, member.function);
+ }
+ } else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
+ if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
+ if (member.variable->getter != nullptr) {
+ member.variable->getter->set_datatype(member.variable->datatype);
+
+ resolve_function_body(member.variable->getter);
+ }
+ if (member.variable->setter != nullptr) {
+ resolve_function_signature(member.variable->setter);
+
+ if (member.variable->setter->parameters.size() > 0) {
+ member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier;
+ member.variable->setter->parameters[0]->set_datatype(member.get_datatype());
+ }
- // Apply annotations.
- for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
- E->apply(parser, member.function);
+ resolve_function_body(member.variable->setter);
+ }
+ }
}
}
@@ -798,17 +920,80 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
resolve_class_body(member.m_class);
}
- // Check unused variables.
+ // Check unused variables and datatypes of property getters and setters.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {
- continue;
- }
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
- parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
- }
+ if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
+ parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
+ }
+#endif
+
+ if (member.variable->property == GDScriptParser::VariableNode::PROP_SETGET) {
+ GDScriptParser::FunctionNode *getter_function = nullptr;
+ GDScriptParser::FunctionNode *setter_function = nullptr;
+
+ bool has_valid_getter = false;
+ bool has_valid_setter = false;
+
+ if (member.variable->getter_pointer != nullptr) {
+ if (p_class->has_function(member.variable->getter_pointer->name)) {
+ getter_function = p_class->get_member(member.variable->getter_pointer->name).function;
+ }
+
+ if (getter_function == nullptr) {
+ push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable);
+
+ } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) {
+ push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
+
+ } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) {
+ push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+
+ } else {
+ has_valid_getter = true;
+
+#ifdef DEBUG_ENABLED
+ if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
+#endif
+ }
+ }
+
+ if (member.variable->setter_pointer != nullptr) {
+ if (p_class->has_function(member.variable->setter_pointer->name)) {
+ setter_function = p_class->get_member(member.variable->setter_pointer->name).function;
+ }
+
+ if (setter_function == nullptr) {
+ push_error(vformat(R"(Setter "%s" not found.)", member.variable->setter_pointer->name), member.variable);
+
+ } else if (setter_function->parameters.size() != 1) {
+ push_error(vformat(R"(Function "%s" cannot be used as setter because of its signature.)", setter_function->identifier->name), member.variable);
+
+ } else if (!is_type_compatible(member.variable->datatype, setter_function->parameters[0]->datatype, true)) {
+ push_error(vformat(R"(Function with argument type "%s" cannot be used as setter for a property of type "%s".)", setter_function->parameters[0]->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+
+ } else {
+ has_valid_setter = true;
+
+#ifdef DEBUG_ENABLED
+ if (member.variable->datatype.builtin_type == Variant::FLOAT && setter_function->parameters[0]->datatype.builtin_type == Variant::INT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
#endif
+ }
+ }
+
+ if (member.variable->datatype.is_variant() && has_valid_getter && has_valid_setter) {
+ if (!is_type_compatible(getter_function->datatype, setter_function->parameters[0]->datatype, true)) {
+ push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable);
+ }
+ }
+ }
+ }
}
}
@@ -885,7 +1070,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
case GDScriptParser::Node::SUBSCRIPT:
case GDScriptParser::Node::TERNARY_OPERATOR:
case GDScriptParser::Node::UNARY_OPERATOR:
- reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node));
+ reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), true);
break;
case GDScriptParser::Node::BREAK:
case GDScriptParser::Node::BREAKPOINT:
@@ -932,7 +1117,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
return_type.is_meta_type = false;
p_function->set_datatype(return_type);
if (p_function->return_type) {
- push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type);
+ if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) {
+ push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ }
}
} else {
GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
@@ -1089,12 +1277,30 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
}
- if (!list_resolved) {
+ GDScriptParser::DataType variable_type;
+ if (list_resolved) {
+ variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ variable_type.kind = GDScriptParser::DataType::BUILTIN;
+ variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
+ p_for->variable->set_datatype(variable_type);
+ } else if (p_for->list) {
resolve_node(p_for->list);
+ if (p_for->list->datatype.has_container_element_type()) {
+ variable_type = p_for->list->datatype.get_container_element_type();
+ variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ } else if (p_for->list->datatype.is_typed_container_type()) {
+ variable_type = p_for->list->datatype.get_typed_container_type();
+ variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ } else {
+ // Last resort
+ // TODO: Must other cases be handled? Must we mark as unsafe?
+ variable_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ variable_type.kind = GDScriptParser::DataType::VARIANT;
+ }
+ }
+ if (p_for->variable) {
+ p_for->variable->set_datatype(variable_type);
}
-
- // TODO: If list is a typed array, the variable should be an element.
- // Also applicable for constant range() (so variable is int or float).
resolve_suite(p_for->loop);
p_for->set_datatype(p_for->loop->get_datatype());
@@ -1382,8 +1588,7 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
}
if (p_parameter->datatype_specifier != nullptr) {
- resolve_datatype(p_parameter->datatype_specifier);
- result = p_parameter->datatype_specifier->get_datatype();
+ result = resolve_datatype(p_parameter->datatype_specifier);
result.is_meta_type = false;
if (p_parameter->default_value != nullptr) {
@@ -1395,18 +1600,30 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
}
}
+ if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) {
+ push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value);
+ }
+
p_parameter->set_datatype(result);
}
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
GDScriptParser::DataType result;
+ GDScriptParser::DataType expected_type;
+ bool has_expected_type = false;
+
+ if (parser->current_function != nullptr) {
+ expected_type = parser->current_function->get_datatype();
+ has_expected_type = true;
+ }
+
if (p_return->return_value != nullptr) {
reduce_expression(p_return->return_value);
if (p_return->return_value->type == GDScriptParser::Node::ARRAY) {
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
- if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
- update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
+ if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+ update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
}
}
result = p_return->return_value->get_datatype();
@@ -1418,30 +1635,31 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result.is_constant = true;
}
- GDScriptParser::DataType function_type = parser->current_function->get_datatype();
- function_type.is_meta_type = false;
- if (function_type.is_hard_type()) {
- if (!is_type_compatible(function_type, result)) {
- // Try other way. Okay but not safe.
- if (!is_type_compatible(result, function_type)) {
- push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), function_type.to_string()), p_return);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_return);
- }
+ if (has_expected_type) {
+ expected_type.is_meta_type = false;
+ if (expected_type.is_hard_type()) {
+ if (!is_type_compatible(expected_type, result)) {
+ // Try other way. Okay but not safe.
+ if (!is_type_compatible(result, expected_type)) {
+ push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_return);
+ }
#ifdef DEBUG_ENABLED
- } else if (function_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
- } else if (result.is_variant()) {
- mark_node_unsafe(p_return);
+ } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
+ } else if (result.is_variant()) {
+ mark_node_unsafe(p_return);
#endif
+ }
}
}
p_return->set_datatype(result);
}
-void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expression) {
+void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expression, bool p_is_root) {
// This one makes some magic happen.
if (p_expression == nullptr) {
@@ -1469,7 +1687,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression));
break;
case GDScriptParser::Node::CALL:
- reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression));
+ reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), p_is_root);
break;
case GDScriptParser::Node::CAST:
reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression));
@@ -1585,6 +1803,7 @@ void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::D
}
}
if (all_same_type) {
+ element_type.is_constant = false;
array_type.set_container_element_type(element_type);
} else if (all_have_type) {
push_error(vformat(R"(Variant array is not compatible with an array of type "%s".)", p_base_type.get_container_element_type().to_string()), p_array_literal);
@@ -1615,7 +1834,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
}
- if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) {
+ if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
bool compatible = true;
GDScriptParser::DataType op_type = assigned_value_type;
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
@@ -1637,7 +1856,6 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} else {
// TODO: Warning in this case.
mark_node_unsafe(p_assignment);
- p_assignment->use_conversion_assign = true;
}
}
} else {
@@ -1665,21 +1883,27 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
identifier->variable_source->set_datatype(id_type);
}
} break;
+ case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
+ GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype();
+ if (!id_type.is_hard_type()) {
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ identifier->parameter_source->set_datatype(id_type);
+ }
+ } break;
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = assigned_value_type;
- id_type.type_source = GDScriptParser::DataType::INFERRED;
- id_type.is_constant = false;
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
identifier->variable_source->set_datatype(id_type);
}
} break;
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = assigned_value_type;
- id_type.type_source = GDScriptParser::DataType::INFERRED;
- id_type.is_constant = false;
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
identifier->variable_source->set_datatype(id_type);
}
} break;
@@ -1705,16 +1929,25 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
p_await->set_datatype(await_type);
return;
}
+
+ GDScriptParser::DataType awaiting_type;
+
if (p_await->to_await->type == GDScriptParser::Node::CALL) {
reduce_call(static_cast<GDScriptParser::CallNode *>(p_await->to_await), true);
+ awaiting_type = p_await->to_await->get_datatype();
} else {
reduce_expression(p_await->to_await);
}
- p_await->is_constant = p_await->to_await->is_constant;
- p_await->reduced_value = p_await->to_await->reduced_value;
+ if (p_await->to_await->is_constant) {
+ p_await->is_constant = p_await->to_await->is_constant;
+ p_await->reduced_value = p_await->to_await->reduced_value;
- GDScriptParser::DataType awaiting_type = p_await->to_await->get_datatype();
+ awaiting_type = p_await->to_await->get_datatype();
+ } else {
+ awaiting_type.kind = GDScriptParser::DataType::VARIANT;
+ awaiting_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ }
p_await->set_datatype(awaiting_type);
@@ -1834,7 +2067,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
p_binary_op->set_datatype(result);
}
-void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_await) {
+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.
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -2140,10 +2373,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
// If the function require typed arrays we must make literals be typed.
- for (Map<int, GDScriptParser::ArrayNode *>::Element *E = arrays.front(); E; E = E->next()) {
- int index = E->key();
+ for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
+ int index = E.key;
if (index < par_types.size() && par_types[index].has_container_element_type()) {
- update_array_literal_element_type(par_types[index], E->get());
+ update_array_literal_element_type(par_types[index], E.value);
}
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
@@ -2193,7 +2426,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
}
- if (call_type.is_coroutine && !is_await) {
+ if (call_type.is_coroutine && !p_is_await && !p_is_root) {
push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee);
}
@@ -2326,13 +2559,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->is_constant = true;
p_identifier->reduced_value = result;
p_identifier->set_datatype(type_from_variant(result, p_identifier));
- } else {
+ } else if (base.is_hard_type()) {
push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
}
} else {
switch (base.builtin_type) {
case Variant::NIL: {
- push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier);
+ if (base.is_hard_type()) {
+ push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier);
+ }
return;
}
case Variant::DICTIONARY: {
@@ -2353,7 +2588,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return;
}
}
- push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ if (base.is_hard_type()) {
+ push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ }
}
}
}
@@ -2374,7 +2611,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
result.enum_type = name;
p_identifier->set_datatype(result);
} else {
- push_error(vformat(R"(Cannot find value "%s" in "%s".)", name, base.to_string()), p_identifier);
+ // Consider as a Dictionary
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy);
}
} else {
push_error(R"(Cannot get property from enum value.)", p_identifier);
@@ -2411,6 +2651,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
case GDScriptParser::ClassNode::Member::VARIABLE:
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE;
p_identifier->variable_source = member.variable;
+ member.variable->usages += 1;
break;
case GDScriptParser::ClassNode::Member::FUNCTION:
resolve_function_signature(member.function);
@@ -2432,14 +2673,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
while (outer != nullptr) {
if (outer->has_member(name)) {
const GDScriptParser::ClassNode::Member &member = outer->get_member(name);
- if (member.type == GDScriptParser::ClassNode::Member::CONSTANT) {
- // TODO: Make sure loops won't cause problem. And make special error message for those.
- // For out-of-order resolution:
- reduce_expression(member.constant->initializer);
- p_identifier->set_datatype(member.get_datatype());
- p_identifier->is_constant = true;
- p_identifier->reduced_value = member.constant->initializer->reduced_value;
- return;
+ switch (member.type) {
+ case GDScriptParser::ClassNode::Member::CONSTANT: {
+ // TODO: Make sure loops won't cause problem. And make special error message for those.
+ // For out-of-order resolution:
+ reduce_expression(member.constant->initializer);
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.constant->initializer->reduced_value;
+ return;
+ } break;
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = member.enum_value.value;
+ return;
+ } break;
+ case GDScriptParser::ClassNode::Member::ENUM: {
+ p_identifier->set_datatype(member.get_datatype());
+ p_identifier->is_constant = false;
+ return;
+ } break;
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ resolve_class_interface(member.m_class);
+ p_identifier->set_datatype(member.m_class->get_datatype());
+ return;
+ } break;
+ default:
+ break;
}
}
outer = outer->outer;
@@ -2452,10 +2713,13 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &native = base.native_type;
if (class_exists(native)) {
- PropertyInfo prop_info;
MethodInfo method_info;
- if (ClassDB::get_property_info(native, name, &prop_info)) {
- p_identifier->set_datatype(type_from_property(prop_info));
+ if (ClassDB::has_property(native, name)) {
+ StringName getter_name = ClassDB::get_property_getter(native, name);
+ MethodBind *getter = ClassDB::get_method(native, getter_name);
+ if (getter != nullptr) {
+ p_identifier->set_datatype(type_from_property(getter->get_return_info()));
+ }
return;
}
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -2727,7 +2991,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
} else {
p_preload->resolved_path = p_preload->path->reduced_value;
// TODO: Save this as script dependency.
- if (p_preload->resolved_path.is_rel_path()) {
+ if (p_preload->resolved_path.is_relative_path()) {
p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path);
}
p_preload->resolved_path = p_preload->resolved_path.simplify_path();
@@ -2737,7 +3001,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
// TODO: Don't load if validating: use completion cache.
p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
if (p_preload->resource.is_null()) {
- push_error(vformat(R"(Could not p_preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
+ push_error(vformat(R"(Could not preload resource file "%s".)", p_preload->resolved_path), p_preload->path);
}
}
}
@@ -2753,6 +3017,9 @@ void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
}
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
+ if (p_subscript->base == nullptr) {
+ return;
+ }
if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
} else {
@@ -2780,7 +3047,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
- if (base_type.is_variant()) {
+ if (base_type.is_variant() || !base_type.is_hard_type()) {
result_type.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_subscript);
} else {
@@ -2903,6 +3170,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type.kind = GDScriptParser::DataType::BUILTIN;
result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
+ if (base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ base_type.builtin_type = Variant::OBJECT;
+ }
switch (base_type.builtin_type) {
// Can't index at all.
case Variant::RID:
@@ -2963,6 +3233,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
+ case Variant::OBJECT:
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
break;
@@ -2977,7 +3248,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
break;
// Here for completeness.
- case Variant::OBJECT:
case Variant::VARIANT_MAX:
break;
}
@@ -3144,7 +3414,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
current = current->_owner;
}
- Ref<GDScriptParserRef> ref = get_parser_for(current->path);
+ Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
+ if (ref.is_null()) {
+ push_error("Could not find script in path.", p_source);
+ GDScriptParser::DataType error_type;
+ error_type.kind = GDScriptParser::DataType::VARIANT;
+ return error_type;
+ }
ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
GDScriptParser::ClassNode *found = ref->get_parser()->head;
@@ -3224,6 +3500,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
} else {
ERR_FAIL_V_MSG(result, "Could not find element type from property hint of a typed array.");
}
+ elem_type.is_constant = false;
result.set_container_element_type(elem_type);
}
}
@@ -3414,9 +3691,27 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context) {
const StringName &name = p_local->name;
GDScriptParser::DataType base = parser->current_class->get_datatype();
-
GDScriptParser::ClassNode *base_class = base.class_type;
+ {
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == name) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ return true;
+ }
+ }
+ if (Variant::has_utility_function(name)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "built-in function");
+ return true;
+ } else if (ClassDB::class_exists(name)) {
+ parser->push_warning(p_local, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_context, name, "global class");
+ return true;
+ }
+ }
+
while (base_class != nullptr) {
if (base_class->has_member(name)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_local->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
@@ -3528,6 +3823,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
return true;
}
+ if (p_source.kind == GDScriptParser::DataType::ENUM) {
+ if (p_source.native_type == p_target.native_type) {
+ return true;
+ }
+ }
if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
return true;
@@ -3656,7 +3956,9 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
} else {
Error err = OK;
ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, parser->script_path);
- depended_parsers[p_path] = ref;
+ if (ref.is_valid()) {
+ depended_parsers[p_path] = ref;
+ }
}
return ref;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 32bf049fa1..ce4525190b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -44,6 +44,12 @@ class GDScriptAnalyzer {
const GDScriptParser::EnumNode *current_enum = nullptr;
List<const GDScriptParser::LambdaNode *> lambda_stack;
+ // Tests for detecting invalid overloading of script members
+ static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node);
+ static _FORCE_INLINE_ bool has_member_name_conflict_in_native_type(const StringName &p_name, const StringName &p_native_type_string);
+ Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string);
+ Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node);
+
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
@@ -72,12 +78,12 @@ class GDScriptAnalyzer {
void resolve_return(GDScriptParser::ReturnNode *p_return);
// Reduction functions.
- void reduce_expression(GDScriptParser::ExpressionNode *p_expression);
+ void reduce_expression(GDScriptParser::ExpressionNode *p_expression, bool p_is_root = false);
void reduce_array(GDScriptParser::ArrayNode *p_array);
void reduce_assignment(GDScriptParser::AssignmentNode *p_assignment);
void reduce_await(GDScriptParser::AwaitNode *p_await);
void reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_op);
- void reduce_call(GDScriptParser::CallNode *p_call, bool is_await = false);
+ void reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await = false, bool p_is_root = false);
void reduce_cast(GDScriptParser::CastNode *p_cast);
void reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
void reduce_get_node(GDScriptParser::GetNodeNode *p_get_node);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 5958326315..6a7e4278d2 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -155,7 +155,7 @@ void GDScriptByteCodeGenerator::end_parameters() {
function->default_arguments.reverse();
}
-void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
+void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) {
function = memnew(GDScriptFunction);
debug_stack = EngineDebugger::is_active();
@@ -209,8 +209,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
if (name_map.size()) {
function->global_names.resize(name_map.size());
function->_global_names_ptr = &function->global_names[0];
- for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
- function->global_names.write[E->get()] = E->key();
+ for (const KeyValue<StringName, int> &E : name_map) {
+ function->global_names.write[E.value] = E.key;
}
function->_global_names_count = function->global_names.size();
@@ -241,8 +241,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->operator_funcs.resize(operator_func_map.size());
function->_operator_funcs_count = function->operator_funcs.size();
function->_operator_funcs_ptr = function->operator_funcs.ptr();
- for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
- function->operator_funcs.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedOperatorEvaluator, int> &E : operator_func_map) {
+ function->operator_funcs.write[E.value] = E.key;
}
} else {
function->_operator_funcs_count = 0;
@@ -253,8 +253,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->setters.resize(setters_map.size());
function->_setters_count = function->setters.size();
function->_setters_ptr = function->setters.ptr();
- for (const Map<Variant::ValidatedSetter, int>::Element *E = setters_map.front(); E; E = E->next()) {
- function->setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedSetter, int> &E : setters_map) {
+ function->setters.write[E.value] = E.key;
}
} else {
function->_setters_count = 0;
@@ -265,8 +265,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->getters.resize(getters_map.size());
function->_getters_count = function->getters.size();
function->_getters_ptr = function->getters.ptr();
- for (const Map<Variant::ValidatedGetter, int>::Element *E = getters_map.front(); E; E = E->next()) {
- function->getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedGetter, int> &E : getters_map) {
+ function->getters.write[E.value] = E.key;
}
} else {
function->_getters_count = 0;
@@ -277,8 +277,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->keyed_setters.resize(keyed_setters_map.size());
function->_keyed_setters_count = function->keyed_setters.size();
function->_keyed_setters_ptr = function->keyed_setters.ptr();
- for (const Map<Variant::ValidatedKeyedSetter, int>::Element *E = keyed_setters_map.front(); E; E = E->next()) {
- function->keyed_setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedKeyedSetter, int> &E : keyed_setters_map) {
+ function->keyed_setters.write[E.value] = E.key;
}
} else {
function->_keyed_setters_count = 0;
@@ -289,8 +289,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->keyed_getters.resize(keyed_getters_map.size());
function->_keyed_getters_count = function->keyed_getters.size();
function->_keyed_getters_ptr = function->keyed_getters.ptr();
- for (const Map<Variant::ValidatedKeyedGetter, int>::Element *E = keyed_getters_map.front(); E; E = E->next()) {
- function->keyed_getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedKeyedGetter, int> &E : keyed_getters_map) {
+ function->keyed_getters.write[E.value] = E.key;
}
} else {
function->_keyed_getters_count = 0;
@@ -301,8 +301,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->indexed_setters.resize(indexed_setters_map.size());
function->_indexed_setters_count = function->indexed_setters.size();
function->_indexed_setters_ptr = function->indexed_setters.ptr();
- for (const Map<Variant::ValidatedIndexedSetter, int>::Element *E = indexed_setters_map.front(); E; E = E->next()) {
- function->indexed_setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedIndexedSetter, int> &E : indexed_setters_map) {
+ function->indexed_setters.write[E.value] = E.key;
}
} else {
function->_indexed_setters_count = 0;
@@ -313,8 +313,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->indexed_getters.resize(indexed_getters_map.size());
function->_indexed_getters_count = function->indexed_getters.size();
function->_indexed_getters_ptr = function->indexed_getters.ptr();
- for (const Map<Variant::ValidatedIndexedGetter, int>::Element *E = indexed_getters_map.front(); E; E = E->next()) {
- function->indexed_getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedIndexedGetter, int> &E : indexed_getters_map) {
+ function->indexed_getters.write[E.value] = E.key;
}
} else {
function->_indexed_getters_count = 0;
@@ -325,8 +325,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->builtin_methods.resize(builtin_method_map.size());
function->_builtin_methods_ptr = function->builtin_methods.ptr();
function->_builtin_methods_count = builtin_method_map.size();
- for (const Map<Variant::ValidatedBuiltInMethod, int>::Element *E = builtin_method_map.front(); E; E = E->next()) {
- function->builtin_methods.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedBuiltInMethod, int> &E : builtin_method_map) {
+ function->builtin_methods.write[E.value] = E.key;
}
} else {
function->_builtin_methods_ptr = nullptr;
@@ -337,8 +337,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->constructors.resize(constructors_map.size());
function->_constructors_ptr = function->constructors.ptr();
function->_constructors_count = constructors_map.size();
- for (const Map<Variant::ValidatedConstructor, int>::Element *E = constructors_map.front(); E; E = E->next()) {
- function->constructors.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedConstructor, int> &E : constructors_map) {
+ function->constructors.write[E.value] = E.key;
}
} else {
function->_constructors_ptr = nullptr;
@@ -349,8 +349,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->utilities.resize(utilities_map.size());
function->_utilities_ptr = function->utilities.ptr();
function->_utilities_count = utilities_map.size();
- for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
- function->utilities.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedUtilityFunction, int> &E : utilities_map) {
+ function->utilities.write[E.value] = E.key;
}
} else {
function->_utilities_ptr = nullptr;
@@ -361,8 +361,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->gds_utilities.resize(gds_utilities_map.size());
function->_gds_utilities_ptr = function->gds_utilities.ptr();
function->_gds_utilities_count = gds_utilities_map.size();
- for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
- function->gds_utilities.write[E->get()] = E->key();
+ for (const KeyValue<GDScriptUtilityFunctions::FunctionPtr, int> &E : gds_utilities_map) {
+ function->gds_utilities.write[E.value] = E.key;
}
} else {
function->_gds_utilities_ptr = nullptr;
@@ -373,8 +373,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->methods.resize(method_bind_map.size());
function->_methods_ptr = function->methods.ptrw();
function->_methods_count = method_bind_map.size();
- for (const Map<MethodBind *, int>::Element *E = method_bind_map.front(); E; E = E->next()) {
- function->methods.write[E->get()] = E->key();
+ for (const KeyValue<MethodBind *, int> &E : method_bind_map) {
+ function->methods.write[E.value] = E.key;
}
} else {
function->_methods_ptr = nullptr;
@@ -385,8 +385,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->lambdas.resize(lambdas_map.size());
function->_lambdas_ptr = function->lambdas.ptrw();
function->_lambdas_count = lambdas_map.size();
- for (const Map<GDScriptFunction *, int>::Element *E = lambdas_map.front(); E; E = E->next()) {
- function->lambdas.write[E->get()] = E->key();
+ for (const KeyValue<GDScriptFunction *, int> &E : lambdas_map) {
+ function->lambdas.write[E.value] = E.key;
}
} else {
function->_lambdas_ptr = nullptr;
@@ -692,7 +692,8 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
if (HAS_BUILTIN_TYPE(p_target)) {
- if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type) &&
+ IS_BUILTIN_TYPE(p_source, Variant::get_indexed_element_type(p_target.type.builtin_type))) {
// Use indexed setter instead.
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
@@ -746,7 +747,8 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address
}
void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) {
+ if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name) &&
+ IS_BUILTIN_TYPE(p_source, Variant::get_member_type(p_target.type.builtin_type, p_name))) {
Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
append(p_target);
@@ -864,6 +866,12 @@ void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_
function->default_arguments.push_back(opcodes.size());
}
+void GDScriptByteCodeGenerator::write_store_global(const Address &p_dst, int p_global_index) {
+ append(GDScriptFunction::OPCODE_STORE_GLOBAL, 1);
+ append(p_dst);
+ append(p_global_index);
+}
+
void GDScriptByteCodeGenerator::write_store_named_global(const Address &p_dst, const StringName &p_global) {
append(GDScriptFunction::OPCODE_STORE_NAMED_GLOBAL, 1);
append(p_dst);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 3d6fb291ad..fbbf5802fd 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -153,12 +153,12 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
#endif
locals.resize(current_locals);
if (debug_stack) {
- for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, int> &E : block_identifiers) {
GDScriptFunction::StackDebug sd;
sd.added = false;
- sd.identifier = E->key();
+ sd.identifier = E.key;
sd.line = current_line;
- sd.pos = E->get();
+ sd.pos = E.value;
stack_debug.push_back(sd);
}
block_identifiers = block_identifier_stack.back()->get();
@@ -419,7 +419,7 @@ public:
virtual void start_block() override;
virtual void end_block() override;
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) override;
virtual GDScriptFunction *write_end() override;
#ifdef DEBUG_ENABLED
@@ -454,6 +454,7 @@ public:
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
+ virtual void write_store_global(const Address &p_dst, int p_global_index) override;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) override;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) override;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 07f50d14dc..b91677d25c 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -51,32 +51,34 @@ GDScriptParser *GDScriptParserRef::get_parser() const {
Error GDScriptParserRef::raise_status(Status p_new_status) {
ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
- Error result = OK;
+ if (result != OK) {
+ return result;
+ }
while (p_new_status > status) {
switch (status) {
case EMPTY:
- result = parser->parse(GDScriptCache::get_source_code(path), path, false);
status = PARSED;
+ result = parser->parse(GDScriptCache::get_source_code(path), path, false);
break;
case PARSED: {
analyzer = memnew(GDScriptAnalyzer(parser));
- Error inheritance_result = analyzer->resolve_inheritance();
status = INHERITANCE_SOLVED;
+ Error inheritance_result = analyzer->resolve_inheritance();
if (result == OK) {
result = inheritance_result;
}
} break;
case INHERITANCE_SOLVED: {
- Error interface_result = analyzer->resolve_interface();
status = INTERFACE_SOLVED;
+ Error interface_result = analyzer->resolve_interface();
if (result == OK) {
result = interface_result;
}
} break;
case INTERFACE_SOLVED: {
- Error body_result = analyzer->resolve_body();
status = FULLY_SOLVED;
+ Error body_result = analyzer->resolve_body();
if (result == OK) {
result = body_result;
}
@@ -86,14 +88,6 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
}
}
if (result != OK) {
- if (parser != nullptr) {
- memdelete(parser);
- parser = nullptr;
- }
- if (analyzer != nullptr) {
- memdelete(analyzer);
- analyzer = nullptr;
- }
return result;
}
}
@@ -123,7 +117,7 @@ void GDScriptCache::remove_script(const String &p_path) {
Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptParserRef::Status p_status, Error &r_error, const String &p_owner) {
MutexLock lock(singleton->lock);
Ref<GDScriptParserRef> ref;
- if (p_owner != String()) {
+ if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
if (singleton->parser_map.has(p_path)) {
@@ -169,7 +163,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const String &p_owner) {
MutexLock lock(singleton->lock);
- if (p_owner != String()) {
+ if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
if (singleton->full_gdscript_cache.has(p_path)) {
@@ -192,7 +186,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_error, const String &p_owner) {
MutexLock lock(singleton->lock);
- if (p_owner != String()) {
+ if (!p_owner.is_empty()) {
singleton->dependencies[p_owner].insert(p_path);
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 943638d29f..9fb661d031 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -54,6 +54,7 @@ private:
GDScriptParser *parser = nullptr;
GDScriptAnalyzer *analyzer = nullptr;
Status status = EMPTY;
+ Error result = OK;
String path;
friend class GDScriptCache;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index ecc86c37f3..e6ecc92d55 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_CODEGEN
#define GDSCRIPT_CODEGEN
-#include "core/io/multiplayer_api.h"
+#include "core/multiplayer/multiplayer.h"
#include "core/string/string_name.h"
#include "core/variant/variant.h"
#include "gdscript_function.h"
@@ -80,7 +80,7 @@ public:
virtual void start_block() = 0;
virtual void end_block() = 0;
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
+ virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, Multiplayer::RPCConfig p_rpc_config, const GDScriptDataType &p_return_type) = 0;
virtual GDScriptFunction *write_end() = 0;
#ifdef DEBUG_ENABLED
@@ -115,6 +115,7 @@ public:
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
+ virtual void write_store_global(const Address &p_dst, int p_global_index) = 0;
virtual void write_store_named_global(const Address &p_dst, const StringName &p_global) = 0;
virtual void write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) = 0;
virtual void write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index ddf4f281b4..bba664c328 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -35,6 +35,8 @@
#include "gdscript_cache.h"
#include "gdscript_utility_functions.h"
+#include "core/config/project_settings.h"
+
bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringName &p_name) {
if (codegen.function_node && codegen.function_node->is_static) {
return false;
@@ -63,7 +65,7 @@ bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringNa
}
void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::Node *p_node) {
- if (error != "") {
+ if (!error.is_empty()) {
return;
}
@@ -107,7 +109,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) {
+ 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);
+ if (is_inner_by_path || is_inner_by_name) {
// Local class.
List<StringName> names;
while (class_type->outer) {
@@ -316,10 +320,21 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
}
+ // Try globals.
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
- int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
- Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
- return codegen.add_constant(global); // TODO: Get type.
+ // If it's an autoload singleton, we postpone to load it at runtime.
+ // This is so one autoload doesn't try to load another before it's compiled.
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype()));
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ gen->write_store_global(global, idx);
+ return global;
+ } else {
+ int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
+ Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
+ return codegen.add_constant(global);
+ }
}
// Try global classes.
@@ -427,7 +442,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
break;
case GDScriptParser::DictionaryNode::LUA_TABLE:
// Lua-style: key is an identifier interpreted as StringName.
- StringName key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name;
+ StringName key = dn->elements[i].key->reduced_value.operator StringName();
element = codegen.add_constant(key);
break;
}
@@ -473,6 +488,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);
GDScriptDataType type = _gdtype_from_datatype(call->get_datatype());
GDScriptCodeGenerator::Address result = codegen.add_temporary(type);
+ GDScriptCodeGenerator::Address nil = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NIL);
+
+ GDScriptCodeGenerator::Address return_addr = p_root ? nil : result;
Vector<GDScriptCodeGenerator::Address> arguments;
for (int i = 0; i < call->arguments.size(); i++) {
@@ -523,13 +541,13 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (within_await) {
gen->write_call_async(result, self, call->function_name, arguments);
} else {
- gen->write_call(result, self, call->function_name, arguments);
+ gen->write_call(return_addr, self, call->function_name, arguments);
}
} else {
if (within_await) {
gen->write_call_self_async(result, call->function_name, arguments);
} else {
- gen->write_call_self(result, call->function_name, arguments);
+ gen->write_call_self(return_addr, call->function_name, arguments);
}
}
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
@@ -564,12 +582,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call_method_bind(result, base, method, arguments);
}
} else {
- gen->write_call(result, base, call->function_name, arguments);
+ gen->write_call(return_addr, base, call->function_name, arguments);
}
} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments);
} else {
- gen->write_call(result, base, call->function_name, arguments);
+ gen->write_call(return_addr, base, call->function_name, arguments);
}
if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -951,7 +969,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Perform operator if any.
if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- GDScriptCodeGenerator::Address value = codegen.add_temporary();
+ GDScriptCodeGenerator::Address value = codegen.add_temporary(_gdtype_from_datatype(subscript->get_datatype()));
if (subscript->is_attribute) {
gen->write_get_named(value, name, prev_base);
} else {
@@ -1006,25 +1024,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
} else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
// Assignment to member property.
- GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- GDScriptCodeGenerator::Address assign_temp = assigned;
+
+ GDScriptCodeGenerator::Address to_assign = assigned_value;
+ bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (has_operation) {
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary();
GDScriptCodeGenerator::Address member = codegen.add_temporary();
gen->write_get_member(member, name);
- gen->write_binary_operator(assigned, assignment->variant_op, member, assigned);
- gen->pop_temporary();
+ gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);
+ gen->pop_temporary(); // Pop member temp.
+ to_assign = op_result;
}
- gen->write_set_member(assigned, name);
+ gen->write_set_member(to_assign, name);
- if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation.
+ }
+ if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression if not done before.
}
} else {
// Regular assignment.
@@ -1058,19 +1083,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
}
- GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
- GDScriptCodeGenerator::Address op_result;
+ GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address to_assign;
+ bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
+ if (has_operation) {
// Perform operation.
- op_result = codegen.add_temporary();
- gen->write_binary_operator(op_result, assignment->variant_op, target, assigned);
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary();
+ GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);
+ gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value);
+ to_assign = op_result;
+
+ if (og_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
} else {
- op_result = assigned;
- assigned = GDScriptCodeGenerator::Address();
+ to_assign = assigned_value;
}
GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
@@ -1078,25 +1109,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (has_setter && !is_in_setter) {
// Call setter.
Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(op_result);
+ args.push_back(to_assign);
gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
} else {
// Just assign.
if (assignment->use_conversion_assign) {
- gen->write_assign_with_conversion(target, op_result);
+ gen->write_assign_with_conversion(target, to_assign);
} else {
- gen->write_assign(target, op_result);
+ gen->write_assign(target, to_assign);
}
}
- if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop assigned value or temp operation result.
}
- if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop assigned value if not done before.
}
if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ gen->pop_temporary(); // Pop the target to assignment.
}
}
return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
@@ -1857,7 +1888,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
StringName func_name;
bool is_static = false;
- MultiplayerAPI::RPCConfig rpc_config;
+ Multiplayer::RPCConfig rpc_config;
GDScriptDataType return_type;
return_type.has_type = true;
return_type.kind = GDScriptDataType::BUILTIN;
@@ -1989,7 +2020,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
if (EngineDebugger::is_active()) {
String signature;
// Path.
- if (p_script->get_path() != String()) {
+ if (!p_script->get_path().is_empty()) {
signature += p_script->get_path();
}
// Location.
@@ -2061,77 +2092,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
Error error = OK;
- CodeGen codegen;
- codegen.generator = memnew(GDScriptByteCodeGenerator);
-
- codegen.class_node = p_class;
- codegen.script = p_script;
- StringName func_name;
+ GDScriptParser::FunctionNode *function;
if (p_is_setter) {
- func_name = "@" + p_variable->identifier->name + "_setter";
+ function = p_variable->setter;
} else {
- func_name = "@" + p_variable->identifier->name + "_getter";
- }
-
- codegen.function_name = func_name;
-
- GDScriptDataType return_type;
- if (p_is_setter) {
- return_type.has_type = true;
- return_type.kind = GDScriptDataType::BUILTIN;
- return_type.builtin_type = Variant::NIL;
- } else {
- return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
- }
-
- codegen.generator->write_start(p_script, func_name, false, MultiplayerAPI::RPCConfig(), return_type);
-
- if (p_is_setter) {
- uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));
- codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype()));
- }
-
- error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter);
- if (error) {
- memdelete(codegen.generator);
- return error;
+ function = p_variable->getter;
}
- GDScriptFunction *gd_function = codegen.generator->write_end();
-
- p_script->member_functions[func_name] = gd_function;
-
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- String signature;
- //path
- if (p_script->get_path() != String()) {
- signature += p_script->get_path();
- }
- //loc
- signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
-
- //function and class
-
- if (p_class->identifier) {
- signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
- } else {
- signature += "::" + String(func_name);
- }
-
- codegen.generator->set_signature(signature);
- }
-#endif
- codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
+ _parse_function(error, p_script, p_class, function);
-#ifdef TOOLS_ENABLED
- p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line;
-#endif
- memdelete(codegen.generator);
-
- return OK;
+ return error;
}
Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
@@ -2174,8 +2146,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->_base = nullptr;
p_script->members.clear();
p_script->constants.clear();
- for (Map<StringName, GDScriptFunction *>::Element *E = p_script->member_functions.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
+ memdelete(E.value);
}
p_script->member_functions.clear();
p_script->member_indices.clear();
@@ -2186,6 +2158,13 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->tool = parser->is_tool();
p_script->name = p_class->identifier ? p_class->identifier->name : "";
+ if (!p_script->name.is_empty()) {
+ if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) {
+ _set_error("The class '" + p_script->name + "' shadows a native class", p_class);
+ return ERR_ALREADY_EXISTS;
+ }
+ }
+
Ref<GDScriptNativeClass> native;
GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type);
@@ -2308,7 +2287,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, constant->initializer->reduced_value);
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = constant->start_line;
- if (constant->doc_description != String()) {
+ if (!constant->doc_description.is_empty()) {
p_script->doc_constants[name] = constant->doc_description;
}
#endif
@@ -2337,28 +2316,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
const GDScriptParser::SignalNode *signal = member.signal;
StringName name = signal->identifier->name;
- GDScript *c = p_script;
-
- while (c) {
- if (c->_signals.has(name)) {
- _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class);
- return ERR_ALREADY_EXISTS;
- }
-
- if (c->base.is_valid()) {
- c = c->base.ptr();
- } else {
- c = nullptr;
- }
- }
-
- if (native.is_valid()) {
- if (ClassDB::has_signal(native->get_name(), name)) {
- _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class);
- return ERR_ALREADY_EXISTS;
- }
- }
-
Vector<StringName> parameters_names;
parameters_names.resize(signal->parameters.size());
for (int j = 0; j < signal->parameters.size(); j++) {
@@ -2515,8 +2472,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
instance->owner = E->get();
//needed for hot reloading
- for (Map<StringName, GDScript::MemberInfo>::Element *F = p_script->member_indices.front(); F; F = F->next()) {
- instance->member_indices_cache[F->key()] = F->get().index;
+ for (const KeyValue<StringName, GDScript::MemberInfo> &F : p_script->member_indices) {
+ instance->member_indices_cache[F.key] = F.value.index;
}
instance->owner->set_script_instance(instance);
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index 1acb9ceddc..9287df2ea0 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -914,6 +914,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr += 5;
} break;
DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE);
+ case OPCODE_STORE_GLOBAL: {
+ text += "store global ";
+ text += DADDR(1);
+ text += " = ";
+ text += String::num_int64(_code_ptr[ip + 2]);
+
+ incr += 3;
+ } break;
case OPCODE_STORE_NAMED_GLOBAL: {
text += "store named global ";
text += DADDR(1);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 70e18c6e6c..ec01c19295 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -173,8 +173,8 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
get_function_names_recursively(cl, "", funcs);
- for (Map<int, String>::Element *E = funcs.front(); E; E = E->next()) {
- r_functions->push_back(E->get() + ":" + itos(E->key()));
+ for (const KeyValue<int, String> &E : funcs) {
+ r_functions->push_back(E.value + ":" + itos(E.key));
}
}
@@ -344,9 +344,9 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
const Map<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
- for (const Map<StringName, GDScript::MemberInfo>::Element *E = mi.front(); E; E = E->next()) {
- p_members->push_back(E->key());
- p_values->push_back(instance->debug_get_member_by_index(E->get().index));
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {
+ p_members->push_back(E.key);
+ p_values->push_back(instance->debug_get_member_by_index(E.value.index));
}
}
@@ -370,14 +370,14 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
List<Pair<String, Variant>> cinfo;
get_public_constants(&cinfo);
- for (const Map<StringName, int>::Element *E = name_idx.front(); E; E = E->next()) {
- if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) {
+ for (const KeyValue<StringName, int> &E : name_idx) {
+ if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {
continue;
}
bool is_script_constant = false;
for (List<Pair<String, Variant>>::Element *CE = cinfo.front(); CE; CE = CE->next()) {
- if (CE->get().first == E->key()) {
+ if (CE->get().first == E.key) {
is_script_constant = true;
break;
}
@@ -386,7 +386,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
continue;
}
- const Variant &var = globals[E->value()];
+ const Variant &var = globals[E.value];
if (Object *obj = var) {
if (Object::cast_to<GDScriptNativeClass>(obj)) {
continue;
@@ -395,7 +395,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
bool skip = false;
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
- if (E->key() == CoreConstants::get_global_constant_name(i)) {
+ if (E.key == CoreConstants::get_global_constant_name(i)) {
skip = true;
break;
}
@@ -404,7 +404,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
continue;
}
- p_globals->push_back(E->key());
+ p_globals->push_back(E.key);
p_values->push_back(var);
}
}
@@ -637,7 +637,7 @@ 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, ScriptCodeCompletionOption> &r_result) {
- if (p_annotation->name == "@export_range" || p_annotation->name == "@export_exp_range") {
+ if (p_annotation->name == "@export_range") {
if (p_argument == 3 || p_argument == 4) {
// Slider hint.
ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -781,6 +781,9 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
if (p_only_functions) {
continue;
}
+ if (r_result.has(member.constant->identifier->name)) {
+ continue;
+ }
option = ScriptCodeCompletionOption(member.constant->identifier->name, ScriptCodeCompletionOption::KIND_CONSTANT);
if (member.constant->initializer) {
option.default_value = member.constant->initializer->reduced_value;
@@ -872,8 +875,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
Map<StringName, Variant> constants;
scr->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ ScriptCodeCompletionOption option(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
r_result.insert(option.display, option);
}
@@ -1086,6 +1089,15 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
kwa++;
}
+ List<StringName> utility_func_names;
+ Variant::get_utility_function_list(&utility_func_names);
+
+ for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ }
+
OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
if (!E.value().is_singleton) {
@@ -1096,12 +1108,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
// Native classes and global constants.
- for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
+ for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) {
ScriptCodeCompletionOption option;
- if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) {
- option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
+ if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {
+ option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CLASS);
} else {
- option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
}
r_result.insert(option.display, option);
}
@@ -1353,7 +1365,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
String arg1 = args[0];
if (arg1.begins_with("/root/")) {
String which = arg1.get_slice("/", 2);
- if (which != "") {
+ if (!which.is_empty()) {
// Try singletons first
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(which)) {
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
@@ -1733,7 +1745,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
}
- if (is_function_parameter && p_context.current_function && p_context.current_class) {
+ if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) {
// Check if it's override of native function, then we can assume the type from the signature.
GDScriptParser::DataType base_type = p_context.current_class->base_type;
while (base_type.is_set()) {
@@ -2219,8 +2231,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (obj) {
List<String> options;
obj->get_argument_options(p_method, p_argidx, &options);
- for (const String &F : options) {
- ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION);
+ for (String &opt : options) {
+ if (opt.is_quoted()) {
+ opt = opt.unquote().quote(quote_style); // Handle user preference.
+ }
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION);
r_result.insert(option.display, option);
}
}
@@ -2319,7 +2334,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptCompletionIdentifier connect_base;
- if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ if (Variant::has_utility_function(call->function_name)) {
+ MethodInfo info = Variant::get_utility_function_info(call->function_name);
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_arghint = _make_arguments_hint(info, p_argidx);
return;
@@ -2640,23 +2659,26 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
} break;
case GDScriptParser::COMPLETION_GET_NODE: {
+ // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.
if (p_owner) {
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
for (const String &E : opts) {
+ r_forced = true;
String opt = E.strip_edges();
if (opt.is_quoted()) {
- r_forced = true;
- String idopt = opt.unquote();
- if (idopt.replace("/", "_").is_valid_identifier()) {
- ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- } else {
- ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- }
+ // Remove quotes so that we can handle user preferred quote style,
+ // or handle NodePaths which are valid identifiers and don't need quotes.
+ opt = opt.unquote();
+ }
+ // The path needs quotes if it's not a valid identifier (with an exception
+ // for "/" as path separator, which also doesn't require quotes).
+ if (!opt.replace("/", "_").is_valid_identifier()) {
+ opt = opt.quote(quote_style); // Handle user preference.
}
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
+ options.insert(option.display, option);
}
// Get autoloads.
@@ -2677,8 +2699,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
} break;
}
- for (Map<String, ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
- r_options->push_back(E->get());
+ for (const KeyValue<String, ScriptCodeCompletionOption> &E : options) {
+ r_options->push_back(E.value);
}
return OK;
@@ -2731,7 +2753,7 @@ void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_t
}
String st = l.substr(tc, l.length()).strip_edges();
- if (st == "" || st.begins_with("#")) {
+ if (st.is_empty() || st.begins_with("#")) {
continue; //ignore!
}
@@ -2788,6 +2810,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION;
r_result.location = base_type.class_type->get_member(p_symbol).get_line();
r_result.class_path = base_type.script_path;
+ r_result.script = GDScriptCache::get_shallow_script(r_result.class_path);
return OK;
}
base_type = base_type.class_type->base_type;
@@ -3078,6 +3101,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
r_result.class_member = p_symbol;
return OK;
}
+ } else {
+ List<StringName> utility_functions;
+ Variant::get_utility_function_list(&utility_functions);
+ if (utility_functions.find(p_symbol) != nullptr) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = p_symbol;
+ return OK;
+ }
}
}
} break;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 876c508689..a3f0c7dfef 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -120,11 +120,11 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
}
List<_GDFKCS> stackpositions;
- for (Map<StringName, _GDFKC>::Element *E = sdmap.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, _GDFKC> &E : sdmap) {
_GDFKCS spp;
- spp.id = E->key();
- spp.order = E->get().order;
- spp.pos = E->get().pos.back()->get();
+ spp.id = E.key;
+ spp.order = E.value.order;
+ spp.pos = E.value.pos.back()->get();
stackpositions.push_back(spp);
}
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 87d8c03494..e1ed71a268 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -192,7 +192,7 @@ public:
GDScriptDataType() = default;
- GDScriptDataType &operator=(const GDScriptDataType &p_other) {
+ void operator=(const GDScriptDataType &p_other) {
kind = p_other.kind;
has_type = p_other.has_type;
builtin_type = p_other.builtin_type;
@@ -203,7 +203,6 @@ public:
if (p_other.has_container_element_type()) {
set_container_element_type(p_other.get_container_element_type());
}
- return *this;
}
GDScriptDataType(const GDScriptDataType &p_other) {
@@ -348,6 +347,7 @@ public:
OPCODE_ITERATE_PACKED_VECTOR3_ARRAY,
OPCODE_ITERATE_PACKED_COLOR_ARRAY,
OPCODE_ITERATE_OBJECT,
+ OPCODE_STORE_GLOBAL,
OPCODE_STORE_NAMED_GLOBAL,
OPCODE_TYPE_ADJUST_BOOL,
OPCODE_TYPE_ADJUST_INT,
@@ -468,7 +468,7 @@ private:
int _initial_line = 0;
bool _static = false;
- MultiplayerAPI::RPCConfig rpc_config;
+ Multiplayer::RPCConfig rpc_config;
GDScript *_script = nullptr;
@@ -588,7 +588,7 @@ public:
void disassemble(const Vector<String> &p_code_lines) const;
#endif
- _FORCE_INLINE_ MultiplayerAPI::RPCConfig get_rpc_config() const { return rpc_config; }
+ _FORCE_INLINE_ Multiplayer::RPCConfig get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index f4c721e00a..48f58e51fe 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -133,7 +133,7 @@ GDScriptParser::GDScriptParser() {
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>);
// Networking.
- register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_AUTHORITY>, 4, true);
+ 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);
// TODO: Warning annotations.
}
@@ -337,12 +337,29 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
tokenizer.set_cursor_position(cursor_line, cursor_column);
script_path = p_script_path;
current = tokenizer.scan();
- // Avoid error as the first token.
- while (current.type == GDScriptTokenizer::Token::ERROR) {
- push_error(current.literal);
+ // Avoid error or newline as the first token.
+ // The latter can mess with the parser when opening files filled exclusively with comments and newlines.
+ while (current.type == GDScriptTokenizer::Token::ERROR || current.type == GDScriptTokenizer::Token::NEWLINE) {
+ if (current.type == GDScriptTokenizer::Token::ERROR) {
+ push_error(current.literal);
+ }
current = tokenizer.scan();
}
+#ifdef DEBUG_ENABLED
+ // Warn about parsing an empty script file:
+ if (current.type == GDScriptTokenizer::Token::TK_EOF) {
+ // Create a dummy Node for the warning, pointing to the very beginning of the file
+ Node *nd = alloc_node<PassNode>();
+ nd->start_line = 1;
+ nd->start_column = 0;
+ nd->end_line = 1;
+ nd->leftmost_column = 0;
+ nd->rightmost_column = 0;
+ push_warning(nd, GDScriptWarning::EMPTY_FILE);
+ }
+#endif
+
push_multiline(false); // Keep one for the whole parsing.
parse_program();
pop_multiline();
@@ -562,12 +579,12 @@ void GDScriptParser::parse_program() {
}
}
- parse_class_body();
+ parse_class_body(true);
#ifdef TOOLS_ENABLED
- for (Map<int, GDScriptTokenizer::CommentData>::Element *E = tokenizer.get_comments().front(); E; E = E->next()) {
- if (E->get().new_line && E->get().comment.begins_with("##")) {
- class_doc_line = MIN(class_doc_line, E->key());
+ for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
+ if (E.value.new_line && E.value.comment.begins_with("##")) {
+ class_doc_line = MIN(class_doc_line, E.key);
}
}
if (has_comment(class_doc_line)) {
@@ -598,9 +615,10 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
}
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after class declaration.)");
- consume(GDScriptTokenizer::Token::NEWLINE, R"(Expected newline after class declaration.)");
- if (!consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
+ bool multiline = match(GDScriptTokenizer::Token::NEWLINE);
+
+ if (multiline && !consume(GDScriptTokenizer::Token::INDENT, R"(Expected indented block after class declaration.)")) {
current_class = previous_class;
return n_class;
}
@@ -613,9 +631,11 @@ GDScriptParser::ClassNode *GDScriptParser::parse_class() {
end_statement("superclass");
}
- parse_class_body();
+ parse_class_body(multiline);
- consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
+ if (multiline) {
+ consume(GDScriptTokenizer::Token::DEDENT, R"(Missing unindent at the end of the class body.)");
+ }
current_class = previous_class;
return n_class;
@@ -720,17 +740,33 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
#endif // TOOLS_ENABLED
if (member->identifier != nullptr) {
- // Enums may be unnamed.
- // TODO: Consider names in outer scope too, for constants and classes (and static functions?)
- if (current_class->members_indices.has(member->identifier->name)) {
- push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
+
+#ifdef DEBUG_ENABLED
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == member->identifier->name) {
+ push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
+ }
+ }
+ if (Variant::has_utility_function(member->identifier->name)) {
+ push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
+ }
+#endif
+
+ if (current_class->members_indices.has(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ } else {
+ current_class->add_member(member);
+ }
} else {
current_class->add_member(member);
}
}
}
-void GDScriptParser::parse_class_body() {
+void GDScriptParser::parse_class_body(bool p_is_multiline) {
bool class_end = false;
while (!class_end && !is_at_end()) {
switch (current.type) {
@@ -776,6 +812,9 @@ void GDScriptParser::parse_class_body() {
if (panic_mode) {
synchronize();
}
+ if (!p_is_multiline) {
+ class_end = true;
+ }
}
}
@@ -924,7 +963,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
switch (p_variable->property) {
- case VariableNode::PROP_INLINE:
+ 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();
@@ -932,9 +971,32 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
- p_variable->setter = parse_suite("setter definition");
- break;
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = "@" + p_variable->identifier->name + "_setter";
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->identifier = identifier;
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ ParameterNode *parameter = alloc_node<ParameterNode>();
+ parameter->identifier = p_variable->setter_parameter;
+
+ if (parameter->identifier != nullptr) {
+ function->parameters_indices[parameter->identifier->name] = 0;
+ function->parameters.push_back(parameter);
+
+ 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;
+ break;
+ }
case VariableNode::PROP_SETGET:
consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")");
make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
@@ -949,11 +1011,25 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
switch (p_variable->property) {
- case VariableNode::PROP_INLINE:
+ case VariableNode::PROP_INLINE: {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
- p_variable->getter = parse_suite("getter definition");
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = "@" + p_variable->identifier->name + "_getter";
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->identifier = identifier;
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ function->body = parse_suite("getter declaration", body);
+
+ p_variable->getter = function;
+ current_function = previous_function;
break;
+ }
case VariableNode::PROP_SETGET:
consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")");
make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
@@ -1036,7 +1112,9 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
SignalNode *signal = alloc_node<SignalNode>();
signal->identifier = parse_identifier();
- if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ push_multiline(true);
+ advance();
do {
if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
// Allow for trailing comma.
@@ -1059,6 +1137,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
}
} while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
+ pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after signal parameters.)*");
}
@@ -1082,13 +1161,31 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
HashMap<StringName, int> elements;
+#ifdef DEBUG_ENABLED
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+#endif
+
do {
if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
break; // Allow trailing comma.
}
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
EnumNode::Value item;
- item.identifier = parse_identifier();
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+#ifdef DEBUG_ENABLED
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
+ }
+#endif
+ item.identifier = identifier;
item.parent_enum = enum_node;
item.line = previous.start_line;
item.leftmost_column = previous.leftmost_column;
@@ -1341,6 +1438,9 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
int error_count = 0;
do {
+ if (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE)) {
+ break;
+ }
Node *statement = parse_statement();
if (statement == nullptr) {
if (error_count++ > 100) {
@@ -1381,7 +1481,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
break;
}
- } while (multiline && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
+ } while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
if (multiline) {
if (!lambda_ended) {
@@ -1591,6 +1691,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
n_for->list = parse_expression(false);
+ if (!n_for->list) {
+ push_error(R"(Expected a list or range after "in".)");
+ }
+
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)");
// Save break/continue state.
@@ -1682,6 +1786,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
MatchBranchNode *branch = parse_match_branch();
if (branch == nullptr) {
+ advance();
continue;
}
@@ -1745,7 +1850,9 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
push_error(R"(No pattern found for "match" branch.)");
}
- consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)");
+ if (!consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "match" patterns.)")) {
+ return nullptr;
+ }
// Save continue state.
bool could_continue = can_continue;
@@ -1778,15 +1885,6 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
PatternNode *pattern = alloc_node<PatternNode>();
switch (current.type) {
- case GDScriptTokenizer::Token::LITERAL:
- advance();
- pattern->pattern_type = PatternNode::PT_LITERAL;
- pattern->literal = parse_literal();
- if (pattern->literal == nullptr) {
- // Error happened.
- return nullptr;
- }
- break;
case GDScriptTokenizer::Token::VAR: {
// Bind.
advance();
@@ -1849,44 +1947,44 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
// Dictionary.
advance();
pattern->pattern_type = PatternNode::PT_DICTIONARY;
-
- if (!check(GDScriptTokenizer::Token::BRACE_CLOSE) && !is_at_end()) {
- do {
- if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
- // Rest.
+ do {
+ if (check(GDScriptTokenizer::Token::BRACE_CLOSE) || is_at_end()) {
+ break;
+ }
+ if (match(GDScriptTokenizer::Token::PERIOD_PERIOD)) {
+ // Rest.
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
+ } else {
+ PatternNode *sub_pattern = alloc_node<PatternNode>();
+ sub_pattern->pattern_type = PatternNode::PT_REST;
+ pattern->dictionary.push_back({ nullptr, sub_pattern });
+ pattern->rest_used = true;
+ }
+ } else {
+ ExpressionNode *key = parse_expression(false);
+ if (key == nullptr) {
+ push_error(R"(Expected expression as key for dictionary pattern.)");
+ }
+ if (match(GDScriptTokenizer::Token::COLON)) {
+ // Value pattern.
+ PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
+ if (sub_pattern == nullptr) {
+ continue;
+ }
if (pattern->rest_used) {
push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
+ } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
+ push_error(R"(The ".." pattern cannot be used as a value.)");
} else {
- PatternNode *sub_pattern = alloc_node<PatternNode>();
- sub_pattern->pattern_type = PatternNode::PT_REST;
- pattern->dictionary.push_back({ nullptr, sub_pattern });
- pattern->rest_used = true;
+ pattern->dictionary.push_back({ key, sub_pattern });
}
} else {
- ExpressionNode *key = parse_expression(false);
- if (key == nullptr) {
- push_error(R"(Expected expression as key for dictionary pattern.)");
- }
- if (match(GDScriptTokenizer::Token::COLON)) {
- // Value pattern.
- PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
- if (sub_pattern == nullptr) {
- continue;
- }
- if (pattern->rest_used) {
- push_error(R"(The ".." pattern must be the last element in the pattern dictionary.)");
- } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
- push_error(R"(The ".." pattern cannot be used as a value.)");
- } else {
- pattern->dictionary.push_back({ key, sub_pattern });
- }
- } else {
- // Key match only.
- pattern->dictionary.push_back({ key, nullptr });
- }
+ // Key match only.
+ pattern->dictionary.push_back({ key, nullptr });
}
- } while (match(GDScriptTokenizer::Token::COMMA));
- }
+ }
+ } while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
break;
}
@@ -1895,8 +1993,13 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
ExpressionNode *expression = parse_expression(false);
if (expression == nullptr) {
push_error(R"(Expected expression for match pattern.)");
+ return nullptr;
} else {
- pattern->pattern_type = PatternNode::PT_EXPRESSION;
+ if (expression->type == GDScriptParser::Node::LITERAL) {
+ pattern->pattern_type = PatternNode::PT_LITERAL;
+ } else {
+ pattern->pattern_type = PatternNode::PT_EXPRESSION;
+ }
pattern->expression = expression;
}
break;
@@ -2107,22 +2210,34 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
operation->operation = UnaryOpNode::OP_NEGATIVE;
operation->variant_op = Variant::OP_NEGATE;
operation->operand = parse_precedence(PREC_SIGN, false);
+ if (operation->operand == nullptr) {
+ push_error(R"(Expected expression after "-" operator.)");
+ }
break;
case GDScriptTokenizer::Token::PLUS:
operation->operation = UnaryOpNode::OP_POSITIVE;
operation->variant_op = Variant::OP_POSITIVE;
operation->operand = parse_precedence(PREC_SIGN, false);
+ if (operation->operand == nullptr) {
+ push_error(R"(Expected expression after "+" operator.)");
+ }
break;
case GDScriptTokenizer::Token::TILDE:
operation->operation = UnaryOpNode::OP_COMPLEMENT;
operation->variant_op = Variant::OP_BIT_NEGATE;
operation->operand = parse_precedence(PREC_BIT_NOT, false);
+ if (operation->operand == nullptr) {
+ push_error(R"(Expected expression after "~" operator.)");
+ }
break;
case GDScriptTokenizer::Token::NOT:
case GDScriptTokenizer::Token::BANG:
operation->operation = UnaryOpNode::OP_LOGIC_NOT;
operation->variant_op = Variant::OP_NOT;
operation->operand = parse_precedence(PREC_LOGIC_NOT, false);
+ if (operation->operand == nullptr) {
+ push_error(vformat(R"(Expected expression after "%s" operator.)", op_type == GDScriptTokenizer::Token::NOT ? "not" : "!"));
+ }
break;
default:
return nullptr; // Unreachable.
@@ -2259,6 +2374,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_ternary_operator(Expressio
operation->false_expr = parse_precedence(PREC_TERNARY, false);
+ if (operation->false_expr == nullptr) {
+ push_error(R"(Expected expression after "else".)");
+ }
+
return operation;
}
@@ -2370,8 +2489,12 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
#ifdef DEBUG_ENABLED
- if (has_operator && source_variable != nullptr && source_variable->assignments == 0) {
- push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name);
+ if (source_variable != nullptr) {
+ if (has_operator && source_variable->assignments == 0) {
+ push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name);
+ }
+
+ source_variable->assignments += 1;
}
#endif
@@ -2386,7 +2509,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_pr
}
await->to_await = element;
- current_function->is_coroutine = true;
+ if (current_function) { // Might be null in a getter or setter.
+ current_function->is_coroutine = true;
+ }
return await;
}
@@ -2450,8 +2575,13 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
switch (dictionary->style) {
case DictionaryNode::LUA_TABLE:
- if (key != nullptr && key->type != Node::IDENTIFIER) {
- push_error("Expected identifier as LUA-style dictionary key.");
+ if (key != nullptr && key->type != Node::IDENTIFIER && key->type != Node::LITERAL) {
+ push_error("Expected identifier or string as LUA-style dictionary key.");
+ advance();
+ break;
+ }
+ if (key != nullptr && key->type == Node::LITERAL && static_cast<LiteralNode *>(key)->value.get_type() != Variant::STRING) {
+ push_error("Expected identifier or string as LUA-style dictionary key.");
advance();
break;
}
@@ -2463,8 +2593,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
push_error(R"(Expected "=" after dictionary key.)");
}
}
- key->is_constant = true;
- key->reduced_value = static_cast<IdentifierNode *>(key)->name;
+ if (key != nullptr) {
+ key->is_constant = true;
+ if (key->type == Node::IDENTIFIER) {
+ key->reduced_value = static_cast<IdentifierNode *>(key)->name;
+ } else if (key->type == Node::LITERAL) {
+ key->reduced_value = StringName(static_cast<LiteralNode *>(key)->value.operator String());
+ }
+ }
break;
case DictionaryNode::PYTHON_DICT:
if (!match(GDScriptTokenizer::Token::COLON)) {
@@ -2543,6 +2679,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_subscript(ExpressionNode *
subscript->base = p_previous_operand;
subscript->index = parse_expression(false);
+ if (subscript->index == nullptr) {
+ push_error(R"(Expected expression after "[".)");
+ }
+
pop_multiline();
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" after subscription index.)");
@@ -2668,6 +2808,23 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
get_node->chain.push_back(identifier);
} while (match(GDScriptTokenizer::Token::SLASH));
return get_node;
+ } else if (match(GDScriptTokenizer::Token::SLASH)) {
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+ IdentifierNode *identifier_root = alloc_node<IdentifierNode>();
+ get_node->chain.push_back(identifier_root);
+ int chain_position = 0;
+ do {
+ make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
+ if (!current.is_node_name()) {
+ push_error(R"(Expect node path after "/".)");
+ return nullptr;
+ }
+ advance();
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
+ get_node->chain.push_back(identifier);
+ } while (match(GDScriptTokenizer::Token::SLASH));
+ return get_node;
} else {
push_error(R"(Expect node path as string or identifier after "$".)");
return nullptr;
@@ -2757,6 +2914,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p
return lambda;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ push_error(R"("yield" was removed in Godot 4.0. Use "await" instead.)");
+ return nullptr;
+}
+
GDScriptParser::ExpressionNode *GDScriptParser::parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign) {
// Just for better error messages.
GDScriptTokenizer::Token::Type invalid = previous.type;
@@ -2907,7 +3069,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
if (!comments.has(p_line)) {
return;
}
- ERR_FAIL_COND(p_brief != "" || p_desc != "" || p_tutorials.size() != 0);
+ ERR_FAIL_COND(!p_brief.is_empty() || !p_desc.is_empty() || p_tutorials.size() != 0);
int line = p_line;
bool in_codeblock = false;
@@ -2939,7 +3101,7 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
String striped_line = doc_line.strip_edges();
// Set the read mode.
- if (striped_line.begins_with("@desc:") && p_desc == "") {
+ if (striped_line.begins_with("@desc:") && p_desc.is_empty()) {
mode = DESC;
striped_line = striped_line.trim_prefix("@desc:");
in_codeblock = _in_codeblock(doc_line, in_codeblock);
@@ -2957,9 +3119,9 @@ void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &
} else {
/* Syntax:
- @tutorial ( The Title Here ) : https://the.url/
- ^ open ^ close ^ colon ^ url
- */
+ * @tutorial ( The Title Here ) : https://the.url/
+ * ^ 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')) {
open_bracket_pos++;
@@ -3113,7 +3275,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, nullptr, PREC_NONE }, // TRAIT,
{ nullptr, nullptr, PREC_NONE }, // VAR,
{ nullptr, nullptr, PREC_NONE }, // VOID,
- { nullptr, nullptr, PREC_NONE }, // YIELD,
+ { &GDScriptParser::parse_yield, nullptr, PREC_NONE }, // YIELD,
// Punctuation
{ &GDScriptParser::parse_array, &GDScriptParser::parse_subscript, PREC_SUBSCRIPT }, // BRACKET_OPEN,
{ nullptr, nullptr, PREC_NONE }, // BRACKET_CLOSE,
@@ -3220,8 +3382,8 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
Variant::construct(parameter.type, r, &(name), 1, error);
p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1);
return false;
}
break;
@@ -3229,13 +3391,13 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
[[fallthrough]];
default: {
if (argument->type != Node::LITERAL) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
return false;
}
Variant value = static_cast<LiteralNode *>(argument)->value;
if (!Variant::can_convert_strict(value.get_type(), parameter.type)) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
return false;
}
Callable::CallError error;
@@ -3244,8 +3406,8 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
Variant::construct(parameter.type, r, &(args), 1, error);
p_annotation->resolved_arguments.push_back(r);
if (error.error != Callable::CallError::CALL_OK) {
- push_error(vformat(R"(Expected %s as argument %d of annotation "%s").)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
- p_annotation->resolved_arguments.remove(p_annotation->resolved_arguments.size() - 1);
+ push_error(vformat(R"(Expected %s as argument %d of annotation "%s".)", Variant::get_type_name(parameter.type), i + 1, p_annotation->name));
+ p_annotation->resolved_arguments.remove_at(p_annotation->resolved_arguments.size() - 1);
return false;
}
break;
@@ -3349,7 +3511,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
String enum_hint_string;
for (const Map<StringName, int>::Element *E = export_type.enum_values.front(); E; E = E->next()) {
- enum_hint_string += E->key().operator String().camelcase_to_underscore(true).capitalize().xml_escape();
+ enum_hint_string += E->key().operator String().capitalize().xml_escape();
enum_hint_string += ":";
enum_hint_string += String::num_int64(E->get()).xml_escape();
@@ -3393,11 +3555,11 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
ERR_FAIL_V_MSG(false, "Not implemented.");
}
-template <MultiplayerAPI::RPCMode t_mode>
+template <Multiplayer::RPCMode t_mode>
bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE && p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to variables and functions.)", p_annotation->name));
- MultiplayerAPI::RPCConfig rpc_config;
+ Multiplayer::RPCConfig rpc_config;
rpc_config.rpc_mode = t_mode;
if (p_annotation->resolved_arguments.size()) {
int last = p_annotation->resolved_arguments.size() - 1;
@@ -3411,29 +3573,29 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
}
for (int i = last; i >= 0; i--) {
String mode = p_annotation->resolved_arguments[i].operator String();
- if (mode == "any") {
- rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_ANY;
- } else if (mode == "auth") {
- rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_AUTHORITY;
- } else if (mode == "sync") {
- rpc_config.sync = true;
- } else if (mode == "nosync") {
- rpc_config.sync = false;
+ if (mode == "any_peer") {
+ rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY_PEER;
+ } else if (mode == "authority") {
+ rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY;
+ } else if (mode == "call_local") {
+ rpc_config.call_local = true;
+ } else if (mode == "call_remote") {
+ rpc_config.call_local = false;
} else if (mode == "reliable") {
- rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
+ rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
} else if (mode == "unreliable") {
- rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
- } else if (mode == "ordered") {
- rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
+ rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE;
+ } else if (mode == "unreliable_ordered") {
+ rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
- push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation);
+ push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
}
}
}
switch (p_node->type) {
case Node::FUNCTION: {
FunctionNode *function = static_cast<FunctionNode *>(p_node);
- if (function->rpc_config.rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
+ if (function->rpc_config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
return false;
}
@@ -3538,6 +3700,39 @@ String GDScriptParser::DataType::to_string() const {
ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
}
+static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
+ switch (p_type) {
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ return Variant::INT;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ return Variant::FLOAT;
+ case Variant::PACKED_STRING_ARRAY:
+ return Variant::STRING;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ return Variant::VECTOR2;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ return Variant::VECTOR3;
+ case Variant::PACKED_COLOR_ARRAY:
+ return Variant::COLOR;
+ default:
+ return Variant::NIL;
+ }
+}
+
+bool GDScriptParser::DataType::is_typed_container_type() const {
+ return kind == GDScriptParser::DataType::BUILTIN && _variant_type_to_typed_array_element_type(builtin_type) != Variant::NIL;
+}
+
+GDScriptParser::DataType GDScriptParser::DataType::get_typed_container_type() const {
+ GDScriptParser::DataType type;
+ type.kind = GDScriptParser::DataType::BUILTIN;
+ type.builtin_type = _variant_type_to_typed_array_element_type(builtin_type);
+ return type;
+}
+
/*---------- PRETTY PRINT FOR DEBUG ----------*/
#ifdef DEBUG_ENABLED
@@ -4330,7 +4525,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
if (p_variable->property == VariableNode::PROP_INLINE) {
push_line(":");
increase_indent();
- print_suite(p_variable->getter);
+ print_suite(p_variable->getter->body);
decrease_indent();
} else {
push_line(" =");
@@ -4350,7 +4545,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
}
push_line("):");
increase_indent();
- print_suite(p_variable->setter);
+ print_suite(p_variable->setter->body);
decrease_indent();
} else {
push_line(" =");
@@ -4389,7 +4584,7 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {
}
print_class(p_parser.get_tree());
- print_line(printed);
+ print_line(String(printed));
}
#endif // DEBUG_ENABLED
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 0bce8d7ddd..2f05b4b948 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -31,8 +31,8 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
-#include "core/io/multiplayer_api.h"
#include "core/io/resource.h"
+#include "core/multiplayer/multiplayer.h"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
#include "core/string/string_name.h"
@@ -161,6 +161,10 @@ public:
container_element_type = nullptr;
}
+ bool is_typed_container_type() const;
+
+ GDScriptParser::DataType get_typed_container_type() const;
+
bool operator==(const DataType &p_other) const {
if (type_source == UNDETECTED || p_other.type_source == UNDETECTED) {
return true; // Can be consireded equal for parsing purposes.
@@ -199,7 +203,7 @@ public:
return !(this->operator==(p_other));
}
- DataType &operator=(const DataType &p_other) {
+ void operator=(const DataType &p_other) {
kind = p_other.kind;
type_source = p_other.type_source;
is_constant = p_other.is_constant;
@@ -217,7 +221,6 @@ public:
if (p_other.has_container_element_type()) {
set_container_element_type(p_other.get_container_element_type());
}
- return *this;
}
DataType() = default;
@@ -729,7 +732,7 @@ public:
SuiteNode *body = nullptr;
bool is_static = false;
bool is_coroutine = false;
- MultiplayerAPI::RPCConfig rpc_config;
+ Multiplayer::RPCConfig rpc_config;
MethodInfo info;
LambdaNode *source_lambda = nullptr;
#ifdef TOOLS_ENABLED
@@ -1105,12 +1108,12 @@ public:
PropertyStyle property = PROP_NONE;
union {
- SuiteNode *setter = nullptr;
+ FunctionNode *setter = nullptr;
IdentifierNode *setter_pointer;
};
IdentifierNode *setter_parameter = nullptr;
union {
- SuiteNode *getter = nullptr;
+ FunctionNode *getter = nullptr;
IdentifierNode *getter_pointer;
};
@@ -1320,7 +1323,7 @@ private:
ClassNode *parse_class();
void parse_class_name();
void parse_extends();
- void parse_class_body();
+ void parse_class_body(bool p_is_multiline);
template <class T>
void parse_class_member(T *(GDScriptParser::*p_parse_function)(), AnnotationInfo::TargetKind p_target, const String &p_member_kind);
SignalNode *parse_signal();
@@ -1340,7 +1343,7 @@ private:
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
- template <MultiplayerAPI::RPCMode t_mode>
+ template <Multiplayer::RPCMode t_mode>
bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
// Statements.
Node *parse_statement();
@@ -1384,6 +1387,7 @@ private:
ExpressionNode *parse_attribute(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_subscript(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_invalid_token(ExpressionNode *p_previous_operand, bool p_can_assign);
TypeNode *parse_type(bool p_allow_void = false);
#ifdef TOOLS_ENABLED
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index d4a098811a..3725fb58f5 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -795,6 +795,15 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
char32_t ch = _peek();
+ if (ch == 0x200E || ch == 0x200F || (ch >= 0x202A && ch <= 0x202E) || (ch >= 0x2066 && ch <= 0x2069)) {
+ Token error = make_error("Invisible text direction control character present in the string, escape it (\"\\u" + String::num_int64(ch, 16) + "\") to avoid confusion.");
+ error.start_column = column;
+ error.leftmost_column = error.start_column;
+ error.end_column = column + 1;
+ error.rightmost_column = error.end_column;
+ push_error(error);
+ }
+
if (ch == '\\') {
// Escape pattern.
_advance();
@@ -1064,7 +1073,8 @@ void GDScriptTokenizer::check_indent() {
// First time indenting, choose character now.
indent_char = current_indent_char;
} else if (current_indent_char != indent_char) {
- Token error = make_error(vformat("Used \"%s\" for indentation instead \"%s\" as used before in the file.", String(&current_indent_char, 1).c_escape(), String(&indent_char, 1).c_escape()));
+ Token error = make_error(vformat("Used %s character for indentation instead of %s as used before in the file.",
+ _get_indent_char_name(current_indent_char), _get_indent_char_name(indent_char)));
error.start_line = line;
error.start_column = 1;
error.leftmost_column = 1;
@@ -1114,6 +1124,12 @@ void GDScriptTokenizer::check_indent() {
}
}
+String GDScriptTokenizer::_get_indent_char_name(char32_t ch) {
+ ERR_FAIL_COND_V(ch != ' ' && ch != '\t', String(&ch, 1).c_escape());
+
+ return ch == ' ' ? "space" : "tab";
+}
+
void GDScriptTokenizer::_skip_whitespace() {
if (pending_indents != 0) {
// Still have some indent/dedent tokens to give.
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 84b82c07f0..b4ee11fd9a 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -233,6 +233,7 @@ private:
bool has_error() const { return !error_stack.is_empty(); }
Token pop_error();
char32_t _advance();
+ String _get_indent_char_name(char32_t ch);
void _skip_whitespace();
void check_indent();
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 62531473c3..e997d3a51e 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -317,9 +317,9 @@ struct GDScriptUtilityFunctionsDefinitions {
d["@subpath"] = cp;
d["@path"] = p->get_path();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
- if (!d.has(E->key())) {
- d[E->key()] = ins->members[E->get().index];
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
+ if (!d.has(E.key)) {
+ d[E.key] = ins->members[E.value.index];
}
}
*r_ret = d;
@@ -396,9 +396,9 @@ struct GDScriptUtilityFunctionsDefinitions {
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
Ref<GDScript> gd_ref = ins->get_script();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if (d.has(E->key())) {
- ins->members.write[E->get().index] = d[E->key()];
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) {
+ if (d.has(E.key)) {
+ ins->members.write[E.value.index] = d[E.key];
}
}
}
@@ -437,9 +437,13 @@ struct GDScriptUtilityFunctionsDefinitions {
str += p_args[i]->operator String();
}
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- if (script->debug_get_stack_level_count() > 0) {
- str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
+ if (Thread::get_caller_id() == Thread::get_main_id()) {
+ ScriptLanguage *script = GDScriptLanguage::get_singleton();
+ if (script->debug_get_stack_level_count() > 0) {
+ str += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()";
+ }
+ } else {
+ str += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id());
}
print_line(str);
@@ -448,15 +452,24 @@ struct GDScriptUtilityFunctionsDefinitions {
static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(0);
+ if (Thread::get_caller_id() != Thread::get_main_id()) {
+ print_line("Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()));
+ return;
+ }
ScriptLanguage *script = GDScriptLanguage::get_singleton();
for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'");
};
+ *r_ret = Variant();
}
static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
VALIDATE_ARG_COUNT(0);
+ if (Thread::get_caller_id() != Thread::get_main_id()) {
+ *r_ret = Array();
+ return;
+ }
ScriptLanguage *script = GDScriptLanguage::get_singleton();
Array ret;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 882256b7e3..be9e5df2b0 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -88,9 +88,9 @@ static String _get_var_type(const Variant *p_var) {
Object *bobj = p_var->get_validated_object_with_check(was_freed);
if (!bobj) {
if (was_freed) {
- basestr = "null instance";
- } else {
basestr = "previously freed";
+ } else {
+ basestr = "null instance";
}
} else {
basestr = bobj->get_class();
@@ -322,6 +322,7 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_ITERATE_PACKED_VECTOR3_ARRAY, \
&&OPCODE_ITERATE_PACKED_COLOR_ARRAY, \
&&OPCODE_ITERATE_OBJECT, \
+ &&OPCODE_STORE_GLOBAL, \
&&OPCODE_STORE_NAMED_GLOBAL, \
&&OPCODE_TYPE_ADJUST_BOOL, \
&&OPCODE_TYPE_ADJUST_INT, \
@@ -530,8 +531,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
- for (const Map<int, Variant::Type>::Element *E = temporary_slots.front(); E; E = E->next()) {
- type_init_function_table[E->get()](&stack[E->key()]);
+ for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
+ type_init_function_table[E.value](&stack[E.key]);
}
String err_text;
@@ -754,7 +755,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
String v = index->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(index) + "'";
@@ -784,7 +785,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
String v = index->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(index) + "'";
@@ -816,7 +817,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (oob) {
String v = index->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(index) + "'";
@@ -847,7 +848,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
String v = index->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(index) + "'";
@@ -883,7 +884,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (!valid) {
String v = key->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(key) + "'";
@@ -916,7 +917,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#ifdef DEBUG_ENABLED
if (oob) {
String v = index->operator String();
- if (v != "") {
+ if (!v.is_empty()) {
v = "'" + v + "'";
} else {
v = "of type '" + _get_var_type(index) + "'";
@@ -1109,7 +1110,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
} else {
#ifdef DEBUG_ENABLED
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
+ "' to a variable of type '" + Variant::get_type_name(var_type) + "'.";
OPCODE_BREAK;
}
} else {
@@ -1131,7 +1132,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (src->get_type() != Variant::ARRAY) {
#ifdef DEBUG_ENABLED
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + +"'.";
+ "' to a variable of type '" + +"'.";
#endif
OPCODE_BREAK;
}
@@ -1157,14 +1158,14 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!nc);
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign value of type '" + Variant::get_type_name(src->get_type()) +
- "' to a variable of type '" + nc->get_name() + "'.";
+ "' to a variable of type '" + nc->get_name() + "'.";
OPCODE_BREAK;
}
Object *src_obj = src->operator Object *();
if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
err_text = "Trying to assign value of type '" + src_obj->get_class_name() +
- "' to a variable of type '" + nc->get_name() + "'.";
+ "' to a variable of type '" + nc->get_name() + "'.";
OPCODE_BREAK;
}
#endif // DEBUG_ENABLED
@@ -1194,7 +1195,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
if (!scr_inst) {
err_text = "Trying to assign value of type '" + src->operator Object *()->get_class_name() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
@@ -1211,7 +1212,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
if (!valid) {
err_text = "Trying to assign value of type '" + src->operator Object *()->get_script_instance()->get_script()->get_path().get_file() +
- "' to a variable of type '" + base_type->get_path().get_file() + "'.";
+ "' to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
}
}
@@ -1231,6 +1232,13 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
+#ifdef DEBUG_ENABLED
+ if (src->operator Object *() && !src->get_validated_object()) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
+#endif
+
Callable::CallError err;
Variant::construct(to_type, *dst, (const Variant **)&src, 1, err);
@@ -1255,6 +1263,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!nc);
#ifdef DEBUG_ENABLED
+ if (src->operator Object *() && !src->get_validated_object()) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Invalid cast: can't convert a non-object value to an object type.";
OPCODE_BREAK;
@@ -1283,6 +1295,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!base_type);
#ifdef DEBUG_ENABLED
+ if (src->operator Object *() && !src->get_validated_object()) {
+ err_text = "Trying to cast a freed object.";
+ OPCODE_BREAK;
+ }
if (src->get_type() != Variant::OBJECT && src->get_type() != Variant::NIL) {
err_text = "Trying to assign a non-object value to a variable of type '" + base_type->get_path().get_file() + "'.";
OPCODE_BREAK;
@@ -1703,6 +1719,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
CHECK_SPACE(3 + instr_arg_count); \
+ ip += instr_arg_count; \
int argc = _code_ptr[ip + 1]; \
GET_INSTRUCTION_ARG(base, argc); \
MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
@@ -2081,8 +2098,10 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
if (result.get_type() != Variant::SIGNAL) {
+ // Not async, return immediately using the target from OPCODE_AWAIT_RESUME.
+ GET_VARIANT_PTR(target, 3);
+ *target = result;
ip += 4; // Skip OPCODE_AWAIT_RESUME and its data.
- // The stack pointer should be the same, so we don't need to set a return value.
is_signal = false;
} else {
sig = result;
@@ -2122,7 +2141,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
retvalue = gdfs;
- Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
+ Error err = sig.connect(callable_bind(Callable(gdfs.ptr(), "_signal_callback"), retvalue), Object::CONNECT_ONESHOT);
if (err != OK) {
err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
OPCODE_BREAK;
@@ -3116,6 +3135,18 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_STORE_GLOBAL) {
+ CHECK_SPACE(3);
+ int global_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(global_idx < 0 || global_idx >= GDScriptLanguage::get_singleton()->get_global_array_size());
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ *dst = GDScriptLanguage::get_singleton()->get_global_array()[global_idx];
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_STORE_NAMED_GLOBAL) {
CHECK_SPACE(3);
int globalname_idx = _code_ptr[ip + 2];
@@ -3264,27 +3295,27 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
//error
// function, file, line, error, explanation
String err_file;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->path != "") {
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->path.is_empty()) {
err_file = p_instance->script->path;
} else if (script) {
err_file = script->path;
}
- if (err_file == "") {
+ if (err_file.is_empty()) {
err_file = "<built-in>";
}
String err_func = name;
- if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && p_instance->script->name != "") {
+ if (p_instance && ObjectDB::get_instance(p_instance->owner_id) != nullptr && p_instance->script->is_valid() && !p_instance->script->name.is_empty()) {
err_func = p_instance->script->name + "." + err_func;
}
int err_line = line;
- if (err_text == "") {
- err_text = "Internal Script Error! - opcode #" + itos(last_opcode) + " (report please).";
+ if (err_text.is_empty()) {
+ err_text = "Internal script error! Opcode: " + itos(last_opcode) + " (please report).";
}
if (!GDScriptLanguage::get_singleton()->debug_break(err_text, false)) {
// debugger break did not happen
- _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, err_text.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _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
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index ad41b60a4e..a351bd6dad 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -145,6 +145,13 @@ String GDScriptWarning::get_message() const {
case REDUNDANT_AWAIT: {
return R"("await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.)";
}
+ case EMPTY_FILE: {
+ return "Empty script file.";
+ }
+ case SHADOWED_GLOBAL_IDENTIFIER: {
+ CHECK_SYMBOLS(3);
+ return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -190,6 +197,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"ASSERT_ALWAYS_TRUE",
"ASSERT_ALWAYS_FALSE",
"REDUNDANT_AWAIT",
+ "EMPTY_FILE",
+ "SHADOWED_GLOBAL_IDENTIFIER",
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 4b295b5eb8..d05f47efe7 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -68,6 +68,8 @@ public:
ASSERT_ALWAYS_TRUE, // Expression for assert argument is always true.
ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
+ EMPTY_FILE, // A script file is empty.
+ SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
WARNING_MAX,
};
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index d106b3b541..80f4721e2d 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -165,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
case ClassNode::Member::VARIABLE: {
lsp::DocumentSymbol symbol;
symbol.name = m.variable->identifier->name;
- symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;
+ symbol.kind = m.variable->property == VariableNode::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;
symbol.deprecated = false;
symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line);
symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column);
@@ -269,7 +269,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
if (j > 0) {
symbol.detail += ", ";
}
- symbol.detail += m.signal->parameters[i]->identifier->name;
+ symbol.detail += m.signal->parameters[j]->identifier->name;
}
symbol.detail += ")";
@@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_text_for_completion(const lsp::Position &p_curs
return longthing;
}
-String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_requred) const {
+String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_required) const {
String longthing;
int len = lines.size();
for (int i = 0; i < len; i++) {
@@ -513,7 +513,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
longthing += first_part;
longthing += String::chr(0xFFFF); //not unicode, represents the cursor
- if (p_func_requred) {
+ if (p_func_required) {
longthing += "("; // tell the parser this is a function call
}
longthing += last_part;
@@ -532,6 +532,9 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const {
ERR_FAIL_INDEX_V(p_position.line, lines.size(), "");
String line = lines[p_position.line];
+ if (line.is_empty()) {
+ return "";
+ }
ERR_FAIL_INDEX_V(p_position.character, line.size(), "");
int start_pos = p_position.character;
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 28b9b3c82a..5d7b16765b 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -85,7 +85,7 @@ public:
Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const;
String get_text_for_completion(const lsp::Position &p_cursor) const;
- String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const;
+ String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_required = false) const;
String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const;
String get_uri() const;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index b6c48468f5..578943696e 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -115,7 +115,7 @@ Error GDScriptLanguageProtocol::LSPeer::send_data() {
// Response sent
if (res_sent >= c_res.size() - 1) {
res_sent = 0;
- res_queue.remove(0);
+ res_queue.remove_at(0);
}
}
return OK;
@@ -212,11 +212,11 @@ void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
lsp::GodotCapabilities capabilities;
DocTools *doc = EditorHelp::get_doc_data();
- for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
+ for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
lsp::GodotNativeClassInfo gdclass;
- gdclass.name = E->get().name;
- gdclass.class_doc = &(E->get());
- if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E->get().name))) {
+ gdclass.name = E.value.name;
+ gdclass.class_doc = &(E.value);
+ if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E.value.name))) {
gdclass.class_info = ptr;
}
capabilities.native_classes.push_back(gdclass);
@@ -284,6 +284,23 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
peer->res_queue.push_back(msg.utf8());
}
+void GDScriptLanguageProtocol::request_client(const String &p_method, const Variant &p_params, int p_client_id) {
+ if (p_client_id == -1) {
+ ERR_FAIL_COND_MSG(latest_client_id == -1,
+ "GDScript LSP: Can't notify client as none was connected.");
+ p_client_id = latest_client_id;
+ }
+ ERR_FAIL_COND(!clients.has(p_client_id));
+ Ref<LSPeer> peer = clients.get(p_client_id);
+ ERR_FAIL_COND(peer == nullptr);
+
+ Dictionary message = make_request(p_method, p_params, next_server_id);
+ next_server_id++;
+ String msg = Variant(message).to_json_string();
+ msg = format_output(msg);
+ peer->res_queue.push_back(msg.utf8());
+}
+
bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {
return bool(_EDITOR_GET("network/language_server/enable_smart_resolve"));
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 5a2dd55c46..a4a63a23a8 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -38,7 +38,7 @@
#include "gdscript_workspace.h"
#include "lsp.hpp"
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For jsonrpc.
#ifdef MODULE_JSONRPC_ENABLED
#include "modules/jsonrpc/jsonrpc.h"
#else
@@ -79,6 +79,8 @@ private:
int latest_client_id = 0;
int next_client_id = 0;
+ int next_server_id = 0;
+
Ref<GDScriptTextDocument> text_document;
Ref<GDScriptWorkspace> workspace;
@@ -107,6 +109,7 @@ public:
void stop();
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
+ void request_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
bool is_smart_resolve_enabled() const;
bool is_goto_native_symbols_enabled() const;
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index c47164d95b..41a2f9e4ad 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_node.h"
GDScriptLanguageServer::GDScriptLanguageServer() {
+ _EDITOR_DEF("network/language_server/remote_host", host);
_EDITOR_DEF("network/language_server/remote_port", port);
_EDITOR_DEF("network/language_server/enable_smart_resolve", true);
_EDITOR_DEF("network/language_server/show_native_symbols_in_editor", false);
@@ -56,9 +57,10 @@ void GDScriptLanguageServer::_notification(int p_what) {
}
} break;
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
+ String host = String(_EDITOR_GET("network/language_server/remote_host"));
int port = (int)_EDITOR_GET("network/language_server/remote_port");
bool use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (port != this->port || use_thread != this->use_thread) {
+ if (host != this->host || port != this->port || use_thread != this->use_thread) {
this->stop();
this->start();
}
@@ -76,9 +78,10 @@ void GDScriptLanguageServer::thread_main(void *p_userdata) {
}
void GDScriptLanguageServer::start() {
+ host = String(_EDITOR_GET("network/language_server/remote_host"));
port = (int)_EDITOR_GET("network/language_server/remote_port");
use_thread = (bool)_EDITOR_GET("network/language_server/use_thread");
- if (protocol.start(port, IPAddress("127.0.0.1")) == OK) {
+ if (protocol.start(port, IPAddress(host)) == OK) {
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
if (use_thread) {
thread_running = true;
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 29c5ddd70e..feee23dd13 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -44,15 +44,14 @@ class GDScriptLanguageServer : public EditorPlugin {
bool thread_running = false;
bool started = false;
bool use_thread = false;
- int port = 6008;
+ String host = "127.0.0.1";
+ int port = 6005;
static void thread_main(void *p_userdata);
private:
void _notification(int p_what);
- void _iteration();
public:
- Error parse_script_file(const String &p_path);
GDScriptLanguageServer();
void start();
void stop();
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 03b1e3fa44..d2e033d7de 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -217,8 +217,8 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
arr = native_member_completions.duplicate();
- for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) {
- ExtendGDScriptParser *script = E->get();
+ for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) {
+ ExtendGDScriptParser *script = E.value;
const Array &items = script->get_member_completions();
const int start_size = arr.size();
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 1512b4bb89..9be9cbc749 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -42,6 +42,7 @@
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal);
ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
@@ -52,6 +53,58 @@ void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
}
+void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) {
+ Ref<Script> script = obj->get_script();
+
+ if (script->get_language()->get_name() != "GDScript") {
+ return;
+ }
+
+ String function_signature = "func " + function;
+ String source = script->get_source_code();
+
+ if (source.find(function_signature) != -1) {
+ return;
+ }
+
+ int first_class = source.find("\nclass ");
+ int start_line = 0;
+ if (first_class != -1) {
+ start_line = source.substr(0, first_class).split("\n").size();
+ } else {
+ start_line = source.split("\n").size();
+ }
+
+ String function_body = "\n\n" + function_signature + "(";
+ for (int i = 0; i < args.size(); ++i) {
+ function_body += args[i];
+ if (i < args.size() - 1) {
+ function_body += ", ";
+ }
+ }
+ function_body += ")";
+ if (EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints")) {
+ function_body += " -> void";
+ }
+ function_body += ":\n\tpass # Replace with function body.\n";
+
+ lsp::TextEdit text_edit;
+
+ if (first_class != -1) {
+ function_body += "\n\n";
+ }
+ text_edit.range.end.line = text_edit.range.start.line = start_line;
+
+ text_edit.newText = function_body;
+
+ String uri = get_file_uri(script->get_path());
+
+ lsp::ApplyWorkspaceEditParams params;
+ params.edit.add_edit(uri, text_edit);
+
+ GDScriptLanguageProtocol::get_singleton()->request_client("workspace/applyEdit", params.to_json());
+}
+
void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
Array files = p_params["files"];
for (int i = 0; i < files.size(); ++i) {
@@ -212,9 +265,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
String query = p_params["query"];
Array arr;
if (!query.is_empty()) {
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
- E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
+ E.value->get_symbols().symbol_tree_as_list(E.key, script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
if (query.is_subsequence_ofi(script_symbols[i].name)) {
lsp::DocumentedSymbolInformation symbol = script_symbols[i];
@@ -233,10 +286,10 @@ Error GDScriptWorkspace::initialize() {
}
DocTools *doc = EditorHelp::get_doc_data();
- for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
- const DocData::ClassDoc &class_data = E->value();
+ for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
+ const DocData::ClassDoc &class_data = E.value;
lsp::DocumentSymbol class_symbol;
- String class_name = E->key();
+ String class_name = E.key;
class_symbol.name = class_name;
class_symbol.native_class = class_name;
class_symbol.kind = lsp::SymbolKind::Class;
@@ -289,7 +342,9 @@ Error GDScriptWorkspace::initialize() {
}
Vector<DocData::MethodDoc> methods_signals;
+ methods_signals.append_array(class_data.constructors);
methods_signals.append_array(class_data.methods);
+ methods_signals.append_array(class_data.operators);
const int signal_start_idx = methods_signals.size();
methods_signals.append_array(class_data.signals);
@@ -344,22 +399,25 @@ Error GDScriptWorkspace::initialize() {
reload_all_workspace_scripts();
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, lsp::DocumentSymbol> &E : native_symbols) {
ClassMembers members;
- const lsp::DocumentSymbol &class_symbol = E->get();
+ const lsp::DocumentSymbol &class_symbol = E.value;
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
members.set(symbol.name, &symbol);
}
- native_members.set(E->key(), members);
+ native_members.set(E.key, members);
}
// cache member completions
- for (Map<String, ExtendGDScriptParser *>::Element *S = scripts.front(); S; S = S->next()) {
- S->get()->get_member_completions();
+ for (const KeyValue<String, ExtendGDScriptParser *> &S : scripts) {
+ S.value->get_member_completions();
}
}
+ EditorNode *editor_node = EditorNode::get_singleton();
+ editor_node->connect("script_add_function_request", callable_mp(this, &GDScriptWorkspace::apply_new_signal));
+
return OK;
}
@@ -551,7 +609,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
}
}
-const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_requred) {
+const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_required) {
const lsp::DocumentSymbol *symbol = nullptr;
String path = get_file_path(p_doc_pos.textDocument.uri);
@@ -576,7 +634,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
} else {
ScriptLanguage::LookupResult ret;
- if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_requred), symbol_identifier, path, nullptr, ret)) {
+ if (symbol_identifier == "new" && parser->get_lines()[p_doc_pos.position.line].replace(" ", "").replace("\t", "").find("new(") > -1) {
+ symbol_identifier = "_init";
+ }
+ if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) {
if (ret.type == ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION) {
String target_script_path = path;
if (!ret.script.is_null()) {
@@ -630,8 +691,8 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
class_ptr = native_members.next(class_ptr);
}
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
- const ExtendGDScriptParser *script = E->get();
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
+ const ExtendGDScriptParser *script = E.value;
const ClassMembers &members = script->get_members();
if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
r_list.push_back(*symbol);
@@ -731,12 +792,12 @@ GDScriptWorkspace::GDScriptWorkspace() {
GDScriptWorkspace::~GDScriptWorkspace() {
Set<String> cached_parsers;
- for (Map<String, ExtendGDScriptParser *>::Element *E = parse_results.front(); E; E = E->next()) {
- cached_parsers.insert(E->key());
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) {
+ cached_parsers.insert(E.key);
}
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
- cached_parsers.insert(E->key());
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
+ cached_parsers.insert(E.key);
}
for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) {
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 9496677449..6f5600b5cf 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -62,6 +62,8 @@ protected:
void list_script_files(const String &p_root_dir, List<String> &r_files);
+ void apply_new_signal(Object *obj, String function, PackedStringArray args);
+
public:
String root;
String root_uri;
@@ -85,7 +87,7 @@ public:
void publish_diagnostics(const String &p_path);
void completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options);
- const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false);
+ const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_required = false);
void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list);
const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params);
void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 9ac6c6bd4e..b12d1f5f3b 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -263,19 +263,29 @@ struct WorkspaceEdit {
*/
Map<String, Vector<TextEdit>> changes;
+ _FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) {
+ if (changes.has(uri)) {
+ changes[uri].push_back(edit);
+ } else {
+ Vector<TextEdit> edits;
+ edits.push_back(edit);
+ changes[uri] = edits;
+ }
+ }
+
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
Dictionary out_changes;
- for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) {
+ for (const KeyValue<String, Vector<TextEdit>> &E : changes) {
Array edits;
- for (int i = 0; i < E->get().size(); ++i) {
+ for (int i = 0; i < E.value.size(); ++i) {
Dictionary text_edit;
- text_edit["range"] = E->get()[i].range.to_json();
- text_edit["newText"] = E->get()[i].newText;
+ text_edit["range"] = E.value[i].range.to_json();
+ text_edit["newText"] = E.value[i].newText;
edits.push_back(text_edit);
}
- out_changes[E->key()] = edits;
+ out_changes[E.key] = edits;
}
dict["changes"] = out_changes;
@@ -348,21 +358,21 @@ struct Command {
namespace TextDocumentSyncKind {
/**
- * Documents should not be synced at all.
- */
+ * Documents should not be synced at all.
+ */
static const int None = 0;
/**
- * Documents are synced by always sending the full content
- * of the document.
- */
+ * Documents are synced by always sending the full content
+ * of the document.
+ */
static const int Full = 1;
/**
- * Documents are synced by sending the full content on open.
- * After that only incremental updates to the document are
- * send.
- */
+ * Documents are synced by sending the full content on open.
+ * After that only incremental updates to the document are
+ * send.
+ */
static const int Incremental = 2;
}; // namespace TextDocumentSyncKind
@@ -657,20 +667,20 @@ struct TextDocumentContentChangeEvent {
// Use namespace instead of enumeration to follow the LSP specifications
namespace DiagnosticSeverity {
/**
- * Reports an error.
- */
+ * Reports an error.
+ */
static const int Error = 1;
/**
- * Reports a warning.
- */
+ * Reports a warning.
+ */
static const int Warning = 2;
/**
- * Reports an information.
- */
+ * Reports an information.
+ */
static const int Information = 3;
/**
- * Reports a hint.
- */
+ * Reports a hint.
+ */
static const int Hint = 4;
}; // namespace DiagnosticSeverity
@@ -861,18 +871,18 @@ static const int TypeParameter = 25;
*/
namespace InsertTextFormat {
/**
- * The primary text to be inserted is treated as a plain string.
- */
+ * The primary text to be inserted is treated as a plain string.
+ */
static const int PlainText = 1;
/**
- * The primary text to be inserted is treated as a snippet.
- *
- * A snippet can define tab stops and placeholders with `$1`, `$2`
- * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
- * the end of the snippet. Placeholders with equal identifiers are linked,
- * that is typing in one will update others too.
- */
+ * The primary text to be inserted is treated as a snippet.
+ *
+ * A snippet can define tab stops and placeholders with `$1`, `$2`
+ * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+ * the end of the snippet. Placeholders with equal identifiers are linked,
+ * that is typing in one will update others too.
+ */
static const int Snippet = 2;
}; // namespace InsertTextFormat
@@ -1322,6 +1332,18 @@ struct DocumentSymbol {
}
};
+struct ApplyWorkspaceEditParams {
+ WorkspaceEdit edit;
+
+ Dictionary to_json() {
+ Dictionary dict;
+
+ dict["edit"] = edit.to_json();
+
+ return dict;
+ }
+};
+
struct NativeSymbolInspectParams {
String native_class;
String symbol_name;
@@ -1337,16 +1359,16 @@ struct NativeSymbolInspectParams {
*/
namespace FoldingRangeKind {
/**
- * Folding range for a comment
- */
+ * Folding range for a comment
+ */
static const String Comment = "comment";
/**
- * Folding range for a imports or includes
- */
+ * Folding range for a imports or includes
+ */
static const String Imports = "imports";
/**
- * Folding range for a region (e.g. `#region`)
- */
+ * Folding range for a region (e.g. `#region`)
+ */
static const String Region = "region";
} // namespace FoldingRangeKind
@@ -1397,20 +1419,20 @@ struct FoldingRange {
*/
namespace CompletionTriggerKind {
/**
- * Completion was triggered by typing an identifier (24x7 code
- * complete), manual invocation (e.g Ctrl+Space) or via API.
- */
+ * Completion was triggered by typing an identifier (24x7 code
+ * complete), manual invocation (e.g Ctrl+Space) or via API.
+ */
static const int Invoked = 1;
/**
- * Completion was triggered by a trigger character specified by
- * the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
- */
+ * Completion was triggered by a trigger character specified by
+ * the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
+ */
static const int TriggerCharacter = 2;
/**
- * Completion was re-triggered as the current completion list is incomplete.
- */
+ * Completion was re-triggered as the current completion list is incomplete.
+ */
static const int TriggerForIncompleteCompletions = 3;
} // namespace CompletionTriggerKind
@@ -1419,8 +1441,8 @@ static const int TriggerForIncompleteCompletions = 3;
*/
struct CompletionContext {
/**
- * How the completion was triggered.
- */
+ * How the completion was triggered.
+ */
int triggerKind = CompletionTriggerKind::TriggerCharacter;
/**
@@ -1884,7 +1906,7 @@ struct GodotNativeClassInfo {
struct GodotCapabilities {
/**
* Native class list
- */
+ */
List<GodotNativeClassInfo> native_classes;
Dictionary to_json() {
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 6225e5d1eb..21883b3c6e 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -133,13 +133,12 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
-
- // Enable all warnings for GDScript, so we can test them.
- ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
- for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
- String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
- ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
- }
+ }
+ // Enable all warnings for GDScript, so we can test them.
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
+ for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
+ String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
}
// Enable printing to show results
@@ -268,7 +267,7 @@ bool GDScriptTestRunner::generate_class_index() {
String base_type;
String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type);
- if (class_name == String()) {
+ if (class_name.is_empty()) {
continue;
}
ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
@@ -335,7 +334,7 @@ void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_e
result->output += p_message + "\n";
}
-void GDScriptTest::error_handler(void *p_this, const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_explanation, ErrorHandlerType p_type) {
+void GDScriptTest::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) {
ErrorHandlerData *data = (ErrorHandlerData *)p_this;
GDScriptTest *self = data->self;
TestResult *result = data->result;
@@ -415,6 +414,7 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
TestResult result;
result.status = GDTEST_OK;
result.output = String();
+ result.passed = false;
Error err = OK;
@@ -496,7 +496,12 @@ GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
}
return result;
}
-
+ // Script files matching this pattern are allowed to not contain a test() function.
+ if (source_file.match("*.notest.gd")) {
+ enable_stdout();
+ result.passed = check_output(result.output);
+ 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) {
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
index 9b2d14a371..98c57dc97c 100644
--- a/modules/gdscript/tests/gdscript_test_runner.h
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -87,7 +87,7 @@ private:
public:
static void print_handler(void *p_this, const String &p_message, bool p_error);
- 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, ErrorHandlerType p_type);
+ 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/errors/bitwise_float_left_operand.gd b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd
new file mode 100644
index 0000000000..9b722ea50a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.gd
@@ -0,0 +1,3 @@
+func test():
+ # Error here.
+ print(2.2 << 4)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out
new file mode 100644
index 0000000000..7dee854d1a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_left_operand.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands to operator <<, float and int.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd
new file mode 100644
index 0000000000..0a4f647f57
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.gd
@@ -0,0 +1,3 @@
+func test():
+ # Error here.
+ print(2 >> 4.4)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out
new file mode 100644
index 0000000000..1edbf47ec0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/bitwise_float_right_operand.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands to operator >>, int and float.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..b84ccdce81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd
@@ -0,0 +1,5 @@
+class Vector2:
+ pass
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..a7c0a29a69
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+const Vector2 = 0
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd
new file mode 100644
index 0000000000..0ad2337c15
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.gd
@@ -0,0 +1,5 @@
+const CONSTANT = 25
+
+
+func test():
+ CONSTANT(123)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out
new file mode 100644
index 0000000000..f4051cd02c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_used_as_function.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Member "CONSTANT" is not a function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd
new file mode 100644
index 0000000000..7a922cd73e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.gd
@@ -0,0 +1,6 @@
+func test():
+ var lua_dict = {
+ a = 1,
+ b = 2,
+ a = 3, # Duplicate isn't allowed.
+ }
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out
new file mode 100644
index 0000000000..ffdfa56645
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Key "a" was already used in this dictionary (at line 3).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd
new file mode 100644
index 0000000000..933e737ac7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.gd
@@ -0,0 +1,6 @@
+func test():
+ var lua_dict_with_string = {
+ a = 1,
+ b = 2,
+ "a" = 3, # Duplicate isn't allowed.
+ }
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out
new file mode 100644
index 0000000000..ffdfa56645
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_lua_with_string.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Key "a" was already used in this dictionary (at line 3).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd
new file mode 100644
index 0000000000..3b8c83e9cb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.gd
@@ -0,0 +1,6 @@
+func test():
+ var python_dict = {
+ "a": 1,
+ "b": 2,
+ "a": 3, # Duplicate isn't allowed.
+ }
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out
new file mode 100644
index 0000000000..ffdfa56645
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/dictionary_duplicate_key_python.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Key "a" was already used in this dictionary (at line 3).
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd
new file mode 100644
index 0000000000..cf9a0ce038
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.gd
@@ -0,0 +1,7 @@
+enum Size {
+ # Error here. Enum values must be integers.
+ S = 0.0,
+}
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out
new file mode 100644
index 0000000000..b315d20508
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_float_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Enum values must be integers.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..930f91b389
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+enum Vector2 { A, B }
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd
new file mode 100644
index 0000000000..cd9b8fabc4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.gd
@@ -0,0 +1,7 @@
+enum Size {
+ # Error here. Enum values must be integers.
+ S = "hello",
+}
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out
new file mode 100644
index 0000000000..b315d20508
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_string_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Enum values must be integers.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd
new file mode 100644
index 0000000000..4346503fc2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.gd
@@ -0,0 +1,6 @@
+func function():
+ pass
+
+
+func test():
+ function = 25
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_used_as_property.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd
new file mode 100644
index 0000000000..b8c0b7a8d3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.gd
@@ -0,0 +1,3 @@
+func test():
+ # Error here. Array indices must be integers.
+ print([0, 1][true])
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out
new file mode 100644
index 0000000000..015ad756f8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_array_index.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid index type "bool" for a base of type "Array".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd
new file mode 100644
index 0000000000..c159e03140
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.gd
@@ -0,0 +1,2 @@
+func test():
+ print(true + true)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out
new file mode 100644
index 0000000000..c1dc7c7d08
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_bool.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands to operator +, bool and bool.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd
new file mode 100644
index 0000000000..6aec2e0796
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.gd
@@ -0,0 +1,2 @@
+func test():
+ print({"hello": "world"} + {"godot": "engine"})
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out
new file mode 100644
index 0000000000..1b4451edbe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_dictionary.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands "Dictionary" and "Dictionary" for "+" operator.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd
new file mode 100644
index 0000000000..eb2a6a0ce7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.gd
@@ -0,0 +1,2 @@
+func test():
+ print("hello" + ["world"])
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out
new file mode 100644
index 0000000000..6d44c6c1bd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_concatenation_mixed.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Invalid operands "String" and "Array" for "+" operator.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd
new file mode 100644
index 0000000000..a7426e88da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.gd
@@ -0,0 +1,5 @@
+func test():
+ var i = 12
+ # Constants must be made of a constant, deterministic expression.
+ # A constant that depends on a variable's value is not a constant expression.
+ const TEST = 13 + i
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out
new file mode 100644
index 0000000000..c40830f123
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/invalid_constant.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Assigned value for constant "TEST" isn't a constant expression.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd
new file mode 100644
index 0000000000..d88c02d6ee
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.gd
@@ -0,0 +1,3 @@
+func test():
+ # Number separators may not be placed at the beginning of a number.
+ var __ = _123
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out
new file mode 100644
index 0000000000..cfb558bf45
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/leading_number_separator.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Identifier "_123" not declared in the current scope.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd
new file mode 100644
index 0000000000..70bdadf291
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.gd
@@ -0,0 +1,6 @@
+func args(a, b):
+ print(a)
+ print(b)
+
+func test():
+ args(1,)
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_argument.out b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out
index fc2a891109..fc2a891109 100644
--- a/modules/gdscript/tests/scripts/parser/errors/missing_argument.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd
new file mode 100644
index 0000000000..f1be6aaa0c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd
@@ -0,0 +1,11 @@
+var _prop : int
+
+# Getter function has wrong return type.
+var prop : String:
+ get = get_prop
+
+func get_prop():
+ return _prop
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out
new file mode 100644
index 0000000000..29eec51ef2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Function with return type "int" cannot be used as getter for a property of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd
new file mode 100644
index 0000000000..dd190157a1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd
@@ -0,0 +1,11 @@
+var _prop : int
+
+# Setter function has wrong argument type.
+var prop : String:
+ set = set_prop
+
+func set_prop(value : int):
+ _prop = value
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out
new file mode 100644
index 0000000000..7a25280d55
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Function with argument type "int" cannot be used as setter for a property of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd
new file mode 100644
index 0000000000..7f2b29222a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd
@@ -0,0 +1,9 @@
+var _prop : int
+
+# Inline getter returns int instead of String.
+var prop : String:
+ get:
+ return _prop
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out
new file mode 100644
index 0000000000..e0adef1bf8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot return value of type "int" because the function return type is "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd
new file mode 100644
index 0000000000..0ce239dbbd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd
@@ -0,0 +1,9 @@
+var _prop : int
+
+# Inline setter assigns String to int.
+var prop : String:
+ set(value):
+ _prop = value
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
new file mode 100644
index 0000000000..bbadf1ce27
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "String" to a target of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd
new file mode 100644
index 0000000000..059d774927
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.gd
@@ -0,0 +1,4 @@
+var property = 25
+
+func test():
+ property()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out
new file mode 100644
index 0000000000..94d6c26a1a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_used_as_function.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Member "property" is not a function.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd
new file mode 100644
index 0000000000..91401d32fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.gd
@@ -0,0 +1,7 @@
+# See also `parser-warnings/shadowed-constant.gd`.
+const TEST = 25
+
+
+func test():
+ # Error here (trying to set a new value to a constant).
+ TEST = 50
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_class_constant.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd
new file mode 100644
index 0000000000..97f3e55e81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.gd
@@ -0,0 +1,5 @@
+func test():
+ const TEST = 25
+
+ # Error here (can't assign a new value to a constant).
+ TEST = 50
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out
new file mode 100644
index 0000000000..5275183da2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/redefine_local_constant.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a new value to a constant.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd
new file mode 100644
index 0000000000..3bbee5f5f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd
@@ -0,0 +1,8 @@
+var with_setter := 0:
+ set(val):
+ var x: String = val
+ with_setter = val
+
+func test():
+ with_setter = 1
+ print(with_setter)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
new file mode 100644
index 0000000000..9eb2a42ccd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "int" cannot be assigned to a variable of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd
new file mode 100644
index 0000000000..722a8fcdb7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.gd
@@ -0,0 +1,11 @@
+# `class` extends RefCounted by default.
+class Say:
+ func say():
+ super()
+ print("say something")
+
+
+func test():
+ # RefCounted doesn't have a `say()` method, so the `super()` call in the method
+ # definition will cause a run-time error.
+ Say.new().say()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out
new file mode 100644
index 0000000000..e3dbf81850
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/super_nonexistent_base_method.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Function "say()" not found in base RefCounted.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..7cba29884c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+var Vector2
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/as.gd b/modules/gdscript/tests/scripts/analyzer/features/as.gd
new file mode 100644
index 0000000000..13a36147c0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/as.gd
@@ -0,0 +1,16 @@
+func test():
+ var some_bool = 5 as bool
+ var some_int = 5 as int
+ var some_float = 5 as float
+ print(typeof(some_bool))
+ print(typeof(some_int))
+ print(typeof(some_float))
+
+ print()
+
+ var some_bool_typed := 5 as bool
+ var some_int_typed := 5 as int
+ var some_float_typed := 5 as float
+ print(typeof(some_bool_typed))
+ print(typeof(some_int_typed))
+ print(typeof(some_float_typed))
diff --git a/modules/gdscript/tests/scripts/analyzer/features/as.out b/modules/gdscript/tests/scripts/analyzer/features/as.out
new file mode 100644
index 0000000000..bcf84aa6f6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/as.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+1
+2
+3
+
+1
+2
+3
diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd
new file mode 100644
index 0000000000..f64dce26c9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.gd
@@ -0,0 +1,9 @@
+func inferred_parameter(param = null):
+ if param == null:
+ param = Node.new()
+ param.name = "Ok"
+ print(param.name)
+ param.free()
+
+func test():
+ inferred_parameter()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
new file mode 100644
index 0000000000..481016138a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 6
+>> UNSAFE_METHOD_ACCESS
+>> The method 'free' is not present on the inferred type 'Variant' (but may be present on a subtype).
+Ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
new file mode 100644
index 0000000000..30e7deb05a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
@@ -0,0 +1,19 @@
+class A:
+ var x = 3
+
+class B:
+ var x = 4
+
+class C:
+ var x = 5
+
+class Test:
+ var a = A.new()
+ var b: B = B.new()
+ var c := C.new()
+
+func test():
+ var test_instance := Test.new()
+ prints(test_instance.a.x)
+ prints(test_instance.b.x)
+ prints(test_instance.c.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out
new file mode 100644
index 0000000000..a078e62cc7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+3
+4
+5
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd
new file mode 100644
index 0000000000..630b20c282
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd
@@ -0,0 +1,11 @@
+# https://github.com/godotengine/godot/issues/43503
+
+var test_var = null
+
+
+func test():
+ print(test_var.x)
+
+
+func _init():
+ test_var = Vector3()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out
new file mode 100644
index 0000000000..94e2ec2af8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+0
diff --git a/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd
new file mode 100644
index 0000000000..135b6c3d85
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.gd
@@ -0,0 +1,16 @@
+extends Node
+
+const NO_TYPE_CONST = 0
+const TYPE_CONST: int = 1
+const GUESS_TYPE_CONST := 2
+
+class Test:
+ var a = NO_TYPE_CONST
+ var b = TYPE_CONST
+ var c = GUESS_TYPE_CONST
+
+func test():
+ var test_instance = Test.new()
+ prints("a", test_instance.a, test_instance.a == NO_TYPE_CONST)
+ prints("b", test_instance.b, test_instance.b == TYPE_CONST)
+ prints("c", test_instance.c, test_instance.c == GUESS_TYPE_CONST)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out
new file mode 100644
index 0000000000..a96bb84246
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/constants_from_parent.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+a 0 true
+b 1 true
+c 2 true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd
new file mode 100644
index 0000000000..5f57c5b8c2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.gd
@@ -0,0 +1,14 @@
+extends Node
+
+enum Named { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+class Test:
+ var a = Named.VALUE_A
+ var b = Named.VALUE_B
+ var c = Named.VALUE_C
+
+func test():
+ var test_instance = Test.new()
+ prints("a", test_instance.a, test_instance.a == Named.VALUE_A)
+ prints("b", test_instance.b, test_instance.b == Named.VALUE_B)
+ prints("c", test_instance.c, test_instance.c == Named.VALUE_C)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out
new file mode 100644
index 0000000000..c160839da3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_from_parent.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+a 0 true
+b 1 true
+c 42 true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd
new file mode 100644
index 0000000000..26edce353d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.gd
@@ -0,0 +1,14 @@
+extends Node
+
+enum { VALUE_A, VALUE_B, VALUE_C = 42 }
+
+class Test:
+ var a = VALUE_A
+ var b = VALUE_B
+ var c = VALUE_C
+
+func test():
+ var test_instance = Test.new()
+ prints("a", test_instance.a, test_instance.a == VALUE_A)
+ prints("b", test_instance.b, test_instance.b == VALUE_B)
+ prints("c", test_instance.c, test_instance.c == VALUE_C)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out
new file mode 100644
index 0000000000..c160839da3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/enum_value_from_parent.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+a 0 true
+b 1 true
+c 42 true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd
new file mode 100644
index 0000000000..b3784dffa3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd
@@ -0,0 +1,14 @@
+# https://github.com/godotengine/godot/issues/41064
+var x = true
+
+func test():
+ var int_var: int = 0
+ var dyn_var = 2
+
+ if x:
+ dyn_var = 5
+ else:
+ dyn_var = Node.new()
+
+ int_var = dyn_var
+ print(int_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out
new file mode 100644
index 0000000000..952029f665
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+5
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd
new file mode 100644
index 0000000000..1706087f82
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd
@@ -0,0 +1,16 @@
+var _prop = 1
+var prop:
+ get = get_prop, set = set_prop
+
+func get_prop():
+ return _prop
+
+func set_prop(value):
+ _prop = value
+
+func test():
+ print(prop)
+
+ prop = 2
+
+ print(prop)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.out b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out
new file mode 100644
index 0000000000..f1253ca57e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd
new file mode 100644
index 0000000000..23eb011b23
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd
@@ -0,0 +1,46 @@
+# Untyped inline property
+var prop1:
+ get:
+ return prop1
+ set(value):
+ prop1 = value
+
+# Typed inline property
+var prop2 : int:
+ get:
+ return prop2
+ set(value):
+ prop2 = value
+
+# Typed inline property with default value
+var prop3 : int = 1:
+ get:
+ return prop3
+ set(value):
+ prop3 = value
+
+# Typed inline property with backing variable
+var _prop4 : int = 2
+var prop4: int:
+ get:
+ return _prop4
+ set(value):
+ _prop4 = value
+
+func test():
+ print(prop1)
+ print(prop2)
+ print(prop3)
+ print(prop4)
+
+ print()
+
+ prop1 = 1
+ prop2 = 2
+ prop3 = 3
+ prop4 = 4
+
+ print(prop1)
+ print(prop2)
+ print(prop3)
+ print(prop4)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
new file mode 100644
index 0000000000..5482592e90
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+null
+0
+1
+2
+
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.gd
new file mode 100644
index 0000000000..569f95850f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.gd
@@ -0,0 +1,2 @@
+func test():
+ print(Color.html_is_valid("00ffff"))
diff --git a/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.out
new file mode 100644
index 0000000000..55482c2b52
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/static_method_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd
new file mode 100644
index 0000000000..f9a8b23b92
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd
@@ -0,0 +1,8 @@
+# https://github.com/godotengine/godot/issues/43221
+extends Node
+
+func test():
+ name = "Node"
+ print(self["name"])
+ self["name"] = "Changed"
+ print(name)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out
new file mode 100644
index 0000000000..6417f4f8da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Node
+Changed
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd
new file mode 100644
index 0000000000..55c40cb971
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.gd
@@ -0,0 +1,6 @@
+# https://github.com/godotengine/godot/issues/53640
+
+func test():
+ var arr := [0]
+ arr[0] = 1
+ print(arr[0])
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out
new file mode 100644
index 0000000000..a7f1357bb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_inferred_access_isnt_constant.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd
new file mode 100644
index 0000000000..9502f6e196
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd
@@ -0,0 +1,10 @@
+class Inner:
+ var prop = "Inner"
+
+
+var array: Array[Inner] = [Inner.new()]
+
+
+func test():
+ var element: Inner = array[0]
+ print(element.prop)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out
new file mode 100644
index 0000000000..8f250d2632
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Inner
diff --git a/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd
new file mode 100644
index 0000000000..b45f99fdd0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.gd
@@ -0,0 +1,3 @@
+func test():
+ # Arrays with consecutive commas are not allowed.
+ var array = ["arrays",,,,]
diff --git a/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out
new file mode 100644
index 0000000000..4ef8526065
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/array_consecutive_commas.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression as array element.
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd
new file mode 100644
index 0000000000..17d5e078e5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.gd
@@ -0,0 +1,2 @@
+func test():
+ var hello == "world"
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out
new file mode 100644
index 0000000000..b150fc0d16
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_2_equal_signs.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected end of statement after variable declaration, found "==" instead.
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd
new file mode 100644
index 0000000000..8b5f620889
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.gd
@@ -0,0 +1,2 @@
+func test():
+ var hello === "world"
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out
new file mode 100644
index 0000000000..b150fc0d16
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_3_equal_signs.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected end of statement after variable declaration, found "==" instead.
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd
new file mode 100644
index 0000000000..8c3a908532
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.gd
@@ -0,0 +1,4 @@
+func test():
+ # Error here.
+ if foo = 25:
+ print(foo)
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out
new file mode 100644
index 0000000000..e8f9130706
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_if.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Assignment is not allowed inside an expression.
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd
new file mode 100644
index 0000000000..126a3227ea
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.gd
@@ -0,0 +1,2 @@
+func test():
+ var hello = "world" = "test"
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out
new file mode 100644
index 0000000000..e8f9130706
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Assignment is not allowed inside an expression.
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd
new file mode 100644
index 0000000000..a99557fa3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.gd
@@ -0,0 +1,4 @@
+func test():
+ # Error here.
+ if var foo = 25:
+ print(foo)
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out
new file mode 100644
index 0000000000..e84f4652ac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_in_var_if.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected conditional expression after "if".
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd
new file mode 100644
index 0000000000..031ea523c8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.gd
@@ -0,0 +1,2 @@
+func test():
+ var = "world"
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out
new file mode 100644
index 0000000000..a4bd8beef1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_without_identifier.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected variable name after "var".
diff --git a/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd
new file mode 100644
index 0000000000..b52a6defcb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.gd
@@ -0,0 +1,2 @@
+func test():
+ print(~)
diff --git a/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out
new file mode 100644
index 0000000000..ceabe42d3c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/binary_complement_without_argument.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "~" operator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd
new file mode 100644
index 0000000000..b3ea1ba1f6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.gd
@@ -0,0 +1,2 @@
+func test():
+ print(not)
diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out
new file mode 100644
index 0000000000..6cf191ea98
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "not" operator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd
new file mode 100644
index 0000000000..8a33079193
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.gd
@@ -0,0 +1,2 @@
+func test():
+ print(!)
diff --git a/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out
new file mode 100644
index 0000000000..87fcc5e2b0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/boolean_negation_without_argument_using_bang.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "!" operator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/brace_syntax.gd b/modules/gdscript/tests/scripts/parser/errors/brace_syntax.gd
new file mode 100644
index 0000000000..ab66537c93
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/brace_syntax.gd
@@ -0,0 +1,3 @@
+func test() {
+ print("Hello world!");
+}
diff --git a/modules/gdscript/tests/scripts/parser/errors/brace_syntax.out b/modules/gdscript/tests/scripts/parser/errors/brace_syntax.out
new file mode 100644
index 0000000000..2f37a740ab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/brace_syntax.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected ":" after function declaration.
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
new file mode 100644
index 0000000000..d13d713454
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
@@ -0,0 +1,6 @@
+# Error here. `class_name` should be used *before* annotations, not after.
+@icon("res://path/to/optional/icon.svg")
+class_name HelloWorld
+
+func test():
+ 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
new file mode 100644
index 0000000000..0bcc8acc55
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+"class_name" should be used before annotations.
diff --git a/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd
new file mode 100644
index 0000000000..49fb4ffedf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.gd
@@ -0,0 +1,3 @@
+func test():
+ var TEST = 50
+ const TEST = 25
diff --git a/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/constant_conflicts_variable.out
new file mode 100644
index 0000000000..407f094ca0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/constant_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/default_value_in_function_call.gd b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd
new file mode 100644
index 0000000000..2581d873dd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.gd
@@ -0,0 +1,7 @@
+func hello(arg1):
+ print(arg1)
+
+
+func test():
+ # Error here.
+ hello(arg1 = 25)
diff --git a/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out
new file mode 100644
index 0000000000..e8f9130706
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/default_value_in_function_call.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Assignment is not allowed inside an expression.
diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
new file mode 100644
index 0000000000..92dfb2366d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.gd
@@ -0,0 +1,2 @@
+func test():
+ var dictionary = { hello = "world",, }
diff --git a/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out
new file mode 100644
index 0000000000..d1dcd1cb4b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/double_dictionary_comma.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression as dictionary key.
diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd
new file mode 100644
index 0000000000..a8f7cf1810
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.gd
@@ -0,0 +1,5 @@
+const test = 25
+
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out
new file mode 100644
index 0000000000..c614acd094
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_constant.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Function "test" has the same name as a previously declared constant.
diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd
new file mode 100644
index 0000000000..5c86710a40
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.gd
@@ -0,0 +1,7 @@
+func test():
+ pass
+
+
+# Error here. The difference with `variable-conflicts-function.gd` is that here,
+# the function is defined *before* the variable.
+var test = 25
diff --git a/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out
new file mode 100644
index 0000000000..551db61531
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/function_conflicts_variable.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Variable "test" has the same name as a previously declared function.
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.gd
new file mode 100644
index 0000000000..3b52f6e324
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.gd
@@ -0,0 +1,2 @@
+func test():
+ var escape = "invalid escape \h <- here"
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.out b/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.out
new file mode 100644
index 0000000000..32b4d004db
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_escape_sequence.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Invalid escape in string.
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd
new file mode 100644
index 0000000000..081b9faf4b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.gd
@@ -0,0 +1,3 @@
+func test():
+ # Error here.
+ var 23test = "is not a valid identifier"
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out
new file mode 100644
index 0000000000..a4bd8beef1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_number.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected variable name after "var".
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd
new file mode 100644
index 0000000000..fa4d6b5cac
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.gd
@@ -0,0 +1,3 @@
+func test():
+ # Error here.
+ var "yes" = "is not a valid identifier"
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out
new file mode 100644
index 0000000000..a4bd8beef1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_identifier_string.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected variable name after "var".
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.gd b/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.gd
new file mode 100644
index 0000000000..c835ce15e1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.gd
@@ -0,0 +1,5 @@
+func test():
+ var amount = 50
+ # C-style ternary operator is invalid in GDScript.
+ # The valid syntax is `"yes" if amount < 60 else "no"`, like in Python.
+ var ternary = amount < 60 ? "yes" : "no"
diff --git a/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.out b/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.out
new file mode 100644
index 0000000000..ac82d691b7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/invalid_ternary_operator.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Unexpected "?" in source. If you want a ternary operator, use "truthy_value if true_condition else falsy_value".
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd b/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd
deleted file mode 100644
index c56ad94095..0000000000
--- a/modules/gdscript/tests/scripts/parser/errors/missing_argument.gd
+++ /dev/null
@@ -1,6 +0,0 @@
-func args(a, b):
- print(a)
- print(b)
-
-func test():
- args(1,)
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
index a1077e1985..8af5f123cc 100644
--- a/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
@@ -1,2 +1,2 @@
func test():
- var a = ("missing paren ->"
+ var a = ("missing paren ->"
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
index 62cb633e9e..0e5e5ce060 100644
--- a/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
@@ -1,3 +1,3 @@
func test():
- if true # Missing colon here.
- print("true")
+ if true # Missing colon here.
+ print("true")
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd
new file mode 100644
index 0000000000..1f66935329
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.gd
@@ -0,0 +1,4 @@
+func test():
+ var x = 1 if false else
+ print("oops")
+ print(x)
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out
new file mode 100644
index 0000000000..dab6b0a1ad
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_expression_after_ternary_else.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "else".
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
index 116b0151da..7a35bf688c 100644
--- a/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
@@ -1,6 +1,6 @@
func args(a, b):
- print(a)
- print(b)
+ print(a)
+ print(b)
func test():
- args(1,2
+ args(1,2
diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd
new file mode 100644
index 0000000000..193f824702
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.gd
@@ -0,0 +1,3 @@
+func test():
+ var a = 0
+ print(a--)
diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out
new file mode 100644
index 0000000000..b6b577a277
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_decrement_operator.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "-" operator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd
new file mode 100644
index 0000000000..035d27638c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.gd
@@ -0,0 +1,3 @@
+func test():
+ var a = 0
+ print(a++)
diff --git a/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out
new file mode 100644
index 0000000000..24eb76593a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mistaken_increment_operator.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "+" operator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
index 6390de9788..31bed2dbc7 100644
--- a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
+++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-Used "\t" for indentation instead " " as used before in the file.
+Used tab character for indentation instead of space as used before in the file.
diff --git a/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd
new file mode 100644
index 0000000000..71a03fbc0d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.gd
@@ -0,0 +1,3 @@
+func test():
+ # Number separators may not be placed right next to each other.
+ var __ = 1__23
diff --git a/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out
new file mode 100644
index 0000000000..71a3c2fd6a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/multiple_number_separators.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Only one underscore can be used as a numeric separator.
diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
index 3875ce3936..df388a21de 100644
--- a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
@@ -1,3 +1,5 @@
extends Node
+
+
func test():
- var a = $ # Expected some node path.
+ var a = $ # Expected some node path.
diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd
new file mode 100644
index 0000000000..c289c9d976
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.gd
@@ -0,0 +1,2 @@
+func test():
+ var while = "it's been a while"
diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out
new file mode 100644
index 0000000000..a4bd8beef1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/redefine_keyword.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected variable name after "var".
diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd
new file mode 100644
index 0000000000..204259f981
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.gd
@@ -0,0 +1,5 @@
+func test():
+ const TEST = 25
+
+ # Error here (can't redeclare a constant on the same scope).
+ const TEST = 50
diff --git a/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out
new file mode 100644
index 0000000000..d67cc92953
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/redefine_local_constant_with_keyword.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a constant named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd
new file mode 100644
index 0000000000..c30c05e4da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.gd
@@ -0,0 +1,3 @@
+func test():
+ var array = [1, 2, 3]
+ array[] = 4
diff --git a/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out
new file mode 100644
index 0000000000..7017c7b4aa
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/subscript_without_index.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected expression after "[".
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd
new file mode 100644
index 0000000000..0d8843df20
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.gd
@@ -0,0 +1,3 @@
+func test():
+ const TEST = 25
+ var TEST = 50
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out
new file mode 100644
index 0000000000..d67cc92953
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_constant.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+There is already a constant named "TEST" declared in this scope.
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd
new file mode 100644
index 0000000000..ce2c8784d6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.gd
@@ -0,0 +1,6 @@
+var test = 25
+
+# Error here. The difference with `variable-conflicts-function.gd` is that here,
+# the function is defined *before* the variable.
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out
new file mode 100644
index 0000000000..daeaca40ec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/variable_conflicts_function.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Function "test" has the same name as a previously declared variable.
diff --git a/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.gd b/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.gd
new file mode 100644
index 0000000000..8850892f2d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.gd
@@ -0,0 +1,13 @@
+# The VCS conflict marker has only 6 `=` signs instead of 7 to prevent editors like
+# Visual Studio Code from recognizing it as an actual VCS conflict marker.
+# Nonetheless, the GDScript parser is still expected to find and report the VCS
+# conflict marker error correctly.
+
+<<<<<<< HEAD
+Hello world
+======
+Goodbye
+>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.out b/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.out
new file mode 100644
index 0000000000..df9dab2223
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/vcs_conflict_marker.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Unexpected "VCS conflict marker" in class body.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
index 6fd2692d47..babe39068c 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
@@ -1,3 +1,5 @@
extends Node
+
+
func test():
- $23 # Can't use number here.
+ $23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
index 1836d42226..b6b1cf3e52 100644
--- a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
@@ -1,3 +1,5 @@
extends Node
+
+
func test():
- $MyNode/23 # Can't use number here.
+ $MyNode/23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.gd b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.gd
new file mode 100644
index 0000000000..7862eff6ec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.gd
@@ -0,0 +1,6 @@
+#GDTEST_PARSER_ERROR
+
+signal event
+
+func test():
+ yield("event")
diff --git a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
new file mode 100644
index 0000000000..36cb699e92
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+"yield" was removed in Godot 4.0. Use "await" instead.
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
new file mode 100644
index 0000000000..43b513045b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.gd
@@ -0,0 +1,34 @@
+func foo(x):
+ match x:
+ 1 + 1:
+ print("1+1")
+ [1,2,[1,{1:2,2:var z,..}]]:
+ print("[1,2,[1,{1:2,2:var z,..}]]")
+ print(z)
+ 1 if true else 2:
+ print("1 if true else 2")
+ 1 < 2:
+ print("1 < 2")
+ 1 or 2 and 1:
+ print("1 or 2 and 1")
+ 6 | 1:
+ print("1 | 1")
+ 1 >> 1:
+ print("1 >> 1")
+ 1, 2 or 3, 4:
+ print("1, 2 or 3, 4")
+ _:
+ print("wildcard")
+
+func test():
+ foo(6 | 1)
+ foo(1 >> 1)
+ foo(2)
+ foo(1)
+ foo(1+1)
+ foo(1 < 2)
+ foo([2, 1])
+ foo(4)
+ foo([1, 2, [1, {1 : 2, 2:3}]])
+ foo([1, 2, [1, {1 : 2, 2:[1,3,5, "123"], 4:2}]])
+ foo([1, 2, [1, {1 : 2}]])
diff --git a/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out
new file mode 100644
index 0000000000..67c7e28046
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/advanced_expression_matching.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+1 | 1
+1 >> 1
+1+1
+1 if true else 2
+1+1
+1 < 2
+wildcard
+1, 2 or 3, 4
+[1,2,[1,{1:2,2:var z,..}]]
+3
+[1,2,[1,{1:2,2:var z,..}]]
+[1, 3, 5, 123]
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/array.gd b/modules/gdscript/tests/scripts/parser/features/array.gd
new file mode 100644
index 0000000000..828ce8d134
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/array.gd
@@ -0,0 +1,16 @@
+func test():
+ # Indexing from the beginning:
+ print([1, 2, 3][0])
+ print([1, 2, 3][1])
+ print([1, 2, 3][2])
+
+ # Indexing from the end:
+ print([1, 2, 3][-1])
+ print([1, 2, 3][-2])
+ print([1, 2, 3][-3])
+
+ # Float indices are currently allowed, but should probably be an error?
+ print([1, 2, 3][0.4])
+ print([1, 2, 3][0.8])
+ print([1, 2, 3][1.0])
+ print([1, 2, 3][-1.0])
diff --git a/modules/gdscript/tests/scripts/parser/features/array.out b/modules/gdscript/tests/scripts/parser/features/array.out
new file mode 100644
index 0000000000..cf576c59e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/array.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+1
+2
+3
+3
+2
+1
+1
+1
+2
+3
diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
new file mode 100644
index 0000000000..2b46f1e88a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.gd
@@ -0,0 +1,27 @@
+func foo(x):
+ match x:
+ 1:
+ print("1")
+ 2:
+ print("2")
+ [1, 2]:
+ print("[1, 2]")
+ 3 or 4:
+ print("3 or 4")
+ 4:
+ print("4")
+ {1 : 2, 2 : 3}:
+ print("{1 : 2, 2 : 3}")
+ _:
+ print("wildcard")
+
+func test():
+ foo(0)
+ foo(1)
+ foo(2)
+ foo([1, 2])
+ foo(3)
+ foo(4)
+ foo([4,4])
+ foo({1 : 2, 2 : 3})
+ foo({1 : 2, 4 : 3})
diff --git a/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out
new file mode 100644
index 0000000000..46ee4b04da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/basic_expression_matching.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+wildcard
+1
+2
+[1, 2]
+wildcard
+4
+wildcard
+{1 : 2, 2 : 3}
+wildcard
diff --git a/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd
new file mode 100644
index 0000000000..de502c6ed1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.gd
@@ -0,0 +1,50 @@
+enum Flags {
+ FIRE = 1 << 1,
+ ICE = 1 << 2,
+ SLIPPERY = 1 << 3,
+ STICKY = 1 << 4,
+ NONSOLID = 1 << 5,
+
+ ALL = FIRE | ICE | SLIPPERY | STICKY | NONSOLID,
+}
+
+
+func test():
+ var flags = Flags.FIRE | Flags.SLIPPERY
+ print(flags)
+
+ flags = Flags.FIRE & Flags.SLIPPERY
+ print(flags)
+
+ flags = Flags.FIRE ^ Flags.SLIPPERY
+ print(flags)
+
+ flags = Flags.ALL & (Flags.FIRE | Flags.ICE)
+ print(flags)
+
+ flags = (Flags.ALL & Flags.FIRE) | Flags.ICE
+ print(flags)
+
+ flags = Flags.ALL & Flags.FIRE | Flags.ICE
+ print(flags)
+
+ # Enum value must be casted to an integer. Otherwise, a parser error is emitted.
+ flags &= int(Flags.ICE)
+ print(flags)
+
+ flags ^= int(Flags.ICE)
+ print(flags)
+
+ flags |= int(Flags.STICKY | Flags.SLIPPERY)
+ print(flags)
+
+ print()
+
+ var num = 2 << 4
+ print(num)
+
+ num <<= 2
+ print(num)
+
+ num >>= 2
+ print(num)
diff --git a/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out
new file mode 100644
index 0000000000..410e358a05
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/bitwise_operators.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+10
+0
+10
+6
+6
+6
+4
+0
+24
+
+32
+128
+32
diff --git a/modules/gdscript/tests/scripts/parser/features/class.gd b/modules/gdscript/tests/scripts/parser/features/class.gd
new file mode 100644
index 0000000000..6652f85ad9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class.gd
@@ -0,0 +1,25 @@
+# Test non-nested/slightly nested class architecture.
+class Test:
+ var number = 25
+ var string = "hello"
+
+
+class TestSub extends Test:
+ var other_string = "bye"
+
+
+class TestConstructor:
+ func _init(argument = 10):
+ print(str("constructor with argument ", argument))
+
+
+func test():
+ var test_instance = Test.new()
+ test_instance.number = 42
+
+ var test_sub = TestSub.new()
+ assert(test_sub.number == 25) # From Test.
+ assert(test_sub.other_string == "bye") # From TestSub.
+
+ TestConstructor.new()
+ TestConstructor.new(500)
diff --git a/modules/gdscript/tests/scripts/parser/features/class.out b/modules/gdscript/tests/scripts/parser/features/class.out
new file mode 100644
index 0000000000..94dc2d6003
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+constructor with argument 10
+constructor with argument 500
diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd b/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd
new file mode 100644
index 0000000000..3f9b4ea86e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance.gd
@@ -0,0 +1,33 @@
+# Test deeply nested class architectures.
+class Test:
+ var depth = 1
+
+ class Nested:
+ var depth_nested = 10
+
+
+class Test2 extends Test:
+ var depth2 = 2
+
+
+class Test3 extends Test2:
+ var depth3 = 3
+
+
+class Test4 extends Test3:
+ var depth4 = 4
+
+ class Nested2:
+ var depth4_nested = 100
+
+
+func test():
+ print(Test.new().depth)
+ print(Test2.new().depth)
+ print(Test2.new().depth2)
+ print(Test3.new().depth)
+ print(Test3.new().depth3)
+ print(Test4.new().depth)
+ print(Test4.new().depth4)
+ print(Test.Nested.new().depth_nested)
+ print(Test4.Nested2.new().depth4_nested)
diff --git a/modules/gdscript/tests/scripts/parser/features/class_inheritance.out b/modules/gdscript/tests/scripts/parser/features/class_inheritance.out
new file mode 100644
index 0000000000..75bdde3d94
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class_inheritance.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+1
+1
+2
+1
+3
+1
+4
+10
+100
diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.gd b/modules/gdscript/tests/scripts/parser/features/class_name.gd
new file mode 100644
index 0000000000..8bd188e247
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class_name.gd
@@ -0,0 +1,5 @@
+class_name HelloWorld
+@icon("res://path/to/optional/icon.svg")
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/class_name.out b/modules/gdscript/tests/scripts/parser/features/class_name.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/class_name.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/concatenation.gd b/modules/gdscript/tests/scripts/parser/features/concatenation.gd
new file mode 100644
index 0000000000..e8335c9823
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/concatenation.gd
@@ -0,0 +1,4 @@
+func test():
+ print(20 + 20)
+ print("hello" + "world")
+ print([1, 2] + [3, 4])
diff --git a/modules/gdscript/tests/scripts/parser/features/concatenation.out b/modules/gdscript/tests/scripts/parser/features/concatenation.out
new file mode 100644
index 0000000000..23bff08f49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/concatenation.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+40
+helloworld
+[1, 2, 3, 4]
diff --git a/modules/gdscript/tests/scripts/parser/features/constants.gd b/modules/gdscript/tests/scripts/parser/features/constants.gd
new file mode 100644
index 0000000000..013c9c074f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/constants.gd
@@ -0,0 +1,11 @@
+func test():
+ const _TEST = 12 + 34 - 56 * 78
+ const _STRING = "yes"
+ const _VECTOR = Vector2(5, 6)
+ const _ARRAY = []
+ const _DICTIONARY = {"this": "dictionary"}
+
+ # Create user constants from built-in constants.
+ const _HELLO = PI + TAU
+ const _INFINITY = INF
+ const _NOT_A_NUMBER = NAN
diff --git a/modules/gdscript/tests/scripts/parser/features/constants.out b/modules/gdscript/tests/scripts/parser/features/constants.out
new file mode 100644
index 0000000000..6093e4a6ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/constants.out
@@ -0,0 +1,33 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '__TEST'
+>> WARNING
+>> Line: 3
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_STRING' is declared but never used in the block. If this is intended, prefix it with an underscore: '__STRING'
+>> WARNING
+>> Line: 4
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_VECTOR' is declared but never used in the block. If this is intended, prefix it with an underscore: '__VECTOR'
+>> WARNING
+>> Line: 5
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_ARRAY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__ARRAY'
+>> WARNING
+>> Line: 6
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_DICTIONARY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__DICTIONARY'
+>> WARNING
+>> Line: 9
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_HELLO' is declared but never used in the block. If this is intended, prefix it with an underscore: '__HELLO'
+>> WARNING
+>> Line: 10
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INFINITY' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INFINITY'
+>> WARNING
+>> Line: 11
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_NOT_A_NUMBER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__NOT_A_NUMBER'
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.gd b/modules/gdscript/tests/scripts/parser/features/dictionary.gd
new file mode 100644
index 0000000000..99afe166c7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary.gd
@@ -0,0 +1,37 @@
+func test():
+ # Non-string keys are valid.
+ print({ 12: "world" }[12])
+
+ var contents = {
+ 0: "zero",
+ 0.0: "zero point zero",
+ null: "null",
+ false: "false",
+ []: "empty array",
+ Vector2i(): "zero Vector2i",
+ 15: {
+ 22: {
+ 4: ["nesting", "arrays"],
+ },
+ },
+ }
+
+ print(contents[0.0])
+ # Making sure declaration order doesn't affect things...
+ print({ 0.0: "zero point zero", 0: "zero", null: "null", false: "false", []: "empty array" }[0])
+ print({ 0.0: "zero point zero", 0: "zero", null: "null", false: "false", []: "empty array" }[0.0])
+
+ print(contents[null])
+ print(contents[false])
+ print(contents[[]])
+ print(contents[Vector2i()])
+ print(contents[15])
+ print(contents[15][22])
+ print(contents[15][22][4])
+ print(contents[15][22][4][0])
+ print(contents[15][22][4][1])
+
+ # Currently fails with "invalid get index 'hello' on base Dictionary".
+ # Both syntaxes are valid however.
+ #print({ "hello": "world" }["hello"])
+ #print({ "hello": "world" }.hello)
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary.out b/modules/gdscript/tests/scripts/parser/features/dictionary.out
new file mode 100644
index 0000000000..54083c1afc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+world
+zero point zero
+zero
+zero point zero
+null
+false
+empty array
+zero Vector2i
+{22:{4:[nesting, arrays]}}
+{4:[nesting, arrays]}
+[nesting, arrays]
+nesting
+arrays
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd
new file mode 100644
index 0000000000..fdd6de2348
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.gd
@@ -0,0 +1,9 @@
+func test():
+ var lua_dict = {
+ a = 1,
+ "b" = 2, # Using strings are allowed too.
+ "with spaces" = 3, # Especially useful when key has spaces...
+ "2" = 4, # ... or invalid identifiers.
+ }
+
+ print(lua_dict)
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
new file mode 100644
index 0000000000..447d7e223c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_lua_style.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+{2:4, a:1, b:2, with spaces:3}
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd
new file mode 100644
index 0000000000..cce8538ddd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.gd
@@ -0,0 +1,12 @@
+func test():
+ # Mixing Python-style and Lua-style syntax in the same dictionary declaration
+ # is allowed.
+ var dict = {
+ "hello": {
+ world = {
+ "is": "beautiful",
+ },
+ },
+ }
+
+ print(dict)
diff --git a/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out
new file mode 100644
index 0000000000..62be807a1f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dictionary_mixed_syntax.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+{hello:{world:{is:beautiful}}}
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd
new file mode 100644
index 0000000000..8ba558e91d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.gd
@@ -0,0 +1,20 @@
+extends Node
+
+
+func test():
+ # Create the required node structure.
+ var hello = Node.new()
+ hello.name = "Hello"
+ add_child(hello)
+ var world = Node.new()
+ world.name = "World"
+ hello.add_child(world)
+
+ # All the ways of writing node paths below with the `$` operator are valid.
+ # Results are assigned to variables to avoid warnings.
+ var __ = $Hello
+ __ = $"Hello"
+ __ = $Hello/World
+ __ = $"Hello/World"
+ __ = $"Hello/.."
+ __ = $"Hello/../Hello/World"
diff --git a/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/dollar_node_paths.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/enum.gd b/modules/gdscript/tests/scripts/parser/features/enum.gd
new file mode 100644
index 0000000000..bbc66f6f3d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/enum.gd
@@ -0,0 +1,14 @@
+enum Size {
+ S = -10,
+ M,
+ L = 0,
+ XL = 10,
+ XXL,
+}
+
+func test():
+ print(Size.S)
+ print(Size.M)
+ print(Size.L)
+ print(Size.XL)
+ print(Size.XXL)
diff --git a/modules/gdscript/tests/scripts/parser/features/enum.out b/modules/gdscript/tests/scripts/parser/features/enum.out
new file mode 100644
index 0000000000..6f3a4a3e49
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/enum.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+-10
+-9
+0
+10
+11
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
new file mode 100644
index 0000000000..1e072728fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
@@ -0,0 +1,18 @@
+@export var example = 99
+@export_range(0, 100) var example_range = 100
+@export_range(0, 100, 1) var example_range_step = 101
+@export_range(0, 100, 1, "or_greater") var example_range_step_or_greater = 102
+
+@export var color: Color
+@export_color_no_alpha var color_no_alpha: Color
+@export_node_path(Sprite2D, Sprite3D, Control, Node) var nodepath := ^"hello"
+
+
+func test():
+ print(example)
+ print(example_range)
+ print(example_range_step)
+ print(example_range_step_or_greater)
+ print(color)
+ print(color_no_alpha)
+ print(nodepath)
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.out b/modules/gdscript/tests/scripts/parser/features/export_variable.out
new file mode 100644
index 0000000000..bae35e75c6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+99
+100
+101
+102
+(0, 0, 0, 1)
+(0, 0, 0, 1)
+hello
diff --git a/modules/gdscript/tests/scripts/parser/features/float_notation.gd b/modules/gdscript/tests/scripts/parser/features/float_notation.gd
new file mode 100644
index 0000000000..b207b88820
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/float_notation.gd
@@ -0,0 +1,24 @@
+func test():
+ # The following floating-point notations are all valid:
+ print(is_equal_approx(123., 123))
+ print(is_equal_approx(.123, 0.123))
+ print(is_equal_approx(.123e4, 1230))
+ print(is_equal_approx(123.e4, 1.23e6))
+ print(is_equal_approx(.123e-1, 0.0123))
+ print(is_equal_approx(123.e-1, 12.3))
+
+ # Same as above, but with negative numbers.
+ print(is_equal_approx(-123., -123))
+ print(is_equal_approx(-.123, -0.123))
+ print(is_equal_approx(-.123e4, -1230))
+ print(is_equal_approx(-123.e4, -1.23e6))
+ print(is_equal_approx(-.123e-1, -0.0123))
+ print(is_equal_approx(-123.e-1, -12.3))
+
+ # Same as above, but with explicit positive numbers (which is redundant).
+ print(is_equal_approx(+123., +123))
+ print(is_equal_approx(+.123, +0.123))
+ print(is_equal_approx(+.123e4, +1230))
+ print(is_equal_approx(+123.e4, +1.23e6))
+ print(is_equal_approx(+.123e-1, +0.0123))
+ print(is_equal_approx(+123.e-1, +12.3))
diff --git a/modules/gdscript/tests/scripts/parser/features/float_notation.out b/modules/gdscript/tests/scripts/parser/features/float_notation.out
new file mode 100644
index 0000000000..041c4439b0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/float_notation.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/for_range.gd b/modules/gdscript/tests/scripts/parser/features/for_range.gd
new file mode 100644
index 0000000000..fd1d002b82
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/for_range.gd
@@ -0,0 +1,39 @@
+func test():
+ for i in range(5):
+ print(i)
+
+ print()
+
+ # Equivalent to the above `for` loop:
+ for i in 5:
+ print(i)
+
+ print()
+
+ for i in range(1, 5):
+ print(i)
+
+ print()
+
+ for i in range(1, -5, -1):
+ print(i)
+
+ print()
+
+ for i in [2, 4, 6, -8]:
+ print(i)
+
+ print()
+
+ for i in [true, false]:
+ print(i)
+
+ print()
+
+ for i in [Vector2i(10, 20), Vector2i(-30, -40)]:
+ print(i)
+
+ print()
+
+ for i in "Hello_Unicôde_world!_🦄":
+ print(i)
diff --git a/modules/gdscript/tests/scripts/parser/features/for_range.out b/modules/gdscript/tests/scripts/parser/features/for_range.out
new file mode 100644
index 0000000000..50b2c856c5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/for_range.out
@@ -0,0 +1,58 @@
+GDTEST_OK
+0
+1
+2
+3
+4
+
+0
+1
+2
+3
+4
+
+1
+2
+3
+4
+
+1
+0
+-1
+-2
+-3
+-4
+
+2
+4
+6
+-8
+
+true
+false
+
+(10, 20)
+(-30, -40)
+
+H
+e
+l
+l
+o
+_
+U
+n
+i
+c
+d
+e
+_
+w
+o
+r
+l
+d
+!
+_
+🦄
diff --git a/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.gd b/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.gd
new file mode 100644
index 0000000000..f5098b00ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.gd
@@ -0,0 +1,5 @@
+func example(_number: int, _number2: int = 5, number3 := 10):
+ return number3
+
+func test():
+ print(example(3))
diff --git a/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.out b/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.out
new file mode 100644
index 0000000000..404cd41fe5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/function_default_parameter_type_inference.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+10
diff --git a/modules/gdscript/tests/scripts/parser/features/function_many_parameters.gd b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.gd
new file mode 100644
index 0000000000..01edb37cec
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.gd
@@ -0,0 +1,5 @@
+func example(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31, arg32, arg33, arg34, arg35, arg36, arg37, arg38, arg39, arg40, arg41, arg42, arg43, arg44, arg45, arg46, arg47, arg48 = false, arg49 = true, arg50 = null):
+ print(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31, arg32, arg33, arg34, arg35, arg36, arg37, arg38, arg39, arg40, arg41, arg42, arg43, arg44, arg45, arg46, arg47, arg48, arg49, arg50)
+
+func test():
+ example(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47)
diff --git a/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out
new file mode 100644
index 0000000000..3a979227d4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/function_many_parameters.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+123456789101112131415161718192212223242526272829303132333435363738394041424344454647falsetruenull
diff --git a/modules/gdscript/tests/scripts/parser/features/in.gd b/modules/gdscript/tests/scripts/parser/features/in.gd
new file mode 100644
index 0000000000..f7296017c5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/in.gd
@@ -0,0 +1,14 @@
+func test():
+ print("dot" in "Godot")
+ print(not "i" in "team")
+
+ print(true in [true, false])
+ print(not null in [true, false])
+ print(null in [null])
+
+ print(26 in [8, 26, 64, 100])
+ print(not Vector2i(10, 20) in [Vector2i(20, 10)])
+
+ print("apple" in { "apple": "fruit" })
+ print("apple" in { "apple": null })
+ print(not "apple" in { "fruit": "apple" })
diff --git a/modules/gdscript/tests/scripts/parser/features/in.out b/modules/gdscript/tests/scripts/parser/features/in.out
new file mode 100644
index 0000000000..7533f6ff54
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/in.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
new file mode 100644
index 0000000000..c3b2506156
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.gd
@@ -0,0 +1,4 @@
+func test():
+ var my_lambda = func(x):
+ print(x)
+ my_lambda.call("hello")
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_callable.out b/modules/gdscript/tests/scripts/parser/features/lambda_callable.out
new file mode 100644
index 0000000000..58774d2d3f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_callable.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+hello
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.gd
new file mode 100644
index 0000000000..f081a0b6a7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.gd
@@ -0,0 +1,4 @@
+func test():
+ var x = 42
+ var my_lambda = func(): print(x)
+ my_lambda.call() # Prints "42".
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.out b/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.out
new file mode 100644
index 0000000000..0982f3718c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_capture_callable.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+42
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.gd b/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.gd
new file mode 100644
index 0000000000..7971ca72a6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.gd
@@ -0,0 +1,10 @@
+func i_take_lambda(lambda: Callable, param: String):
+ lambda.call(param)
+
+
+func test():
+ var my_lambda := func this_is_lambda(x):
+ print("Hello")
+ print("This is %s" % x)
+
+ i_take_lambda(my_lambda, "a lambda")
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.out b/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.out
new file mode 100644
index 0000000000..c627187d82
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_named_callable.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Hello
+This is a lambda
diff --git a/modules/gdscript/tests/scripts/parser/features/match.gd b/modules/gdscript/tests/scripts/parser/features/match.gd
new file mode 100644
index 0000000000..4d05490aa5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match.gd
@@ -0,0 +1,18 @@
+func test():
+ var i = "Hello"
+ match i:
+ "Hello":
+ print("hello")
+ # This will fall through to the default case below.
+ continue
+ "Good bye":
+ print("bye")
+ _:
+ print("default")
+
+ var j = 25
+ match j:
+ 26:
+ print("This won't match")
+ _:
+ print("This will match")
diff --git a/modules/gdscript/tests/scripts/parser/features/match.out b/modules/gdscript/tests/scripts/parser/features/match.out
new file mode 100644
index 0000000000..732885c7a2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/match.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+hello
+default
+This will match
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd
new file mode 100644
index 0000000000..3b30998853
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.gd
@@ -0,0 +1,7 @@
+func test():
+ var __ = [
+ "this",
+ "is", "a","multiline",
+
+ "array", "with mixed indentation and trailing comma",
+ ]
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_arrays.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd
new file mode 100644
index 0000000000..e108cd23d4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.gd
@@ -0,0 +1,10 @@
+func test():
+ var __ = {
+ "multiline": "dictionary","should": "work",
+ "even with": "a trailing comma",
+ }
+
+ __ = {
+ this_also_applies = "to the",
+ lua_style_syntax = null, foo = null,
+ }
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_dictionaries.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_if.gd b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd
new file mode 100644
index 0000000000..86152f4543
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_if.gd
@@ -0,0 +1,14 @@
+func test():
+ # Line breaks are allowed within parentheses.
+ if (
+ 1 == 1
+ and 2 == 2 and
+ 3 == 3
+ ):
+ pass
+
+ # Alternatively, backslashes can be used.
+ if 1 == 1 \
+ and 2 == 2 and \
+ 3 == 3:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_if.out b/modules/gdscript/tests/scripts/parser/features/multiline_if.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_if.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd b/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd
new file mode 100644
index 0000000000..7f5bba85e7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_strings.gd
@@ -0,0 +1,15 @@
+func test():
+ var __ = """
+ This is a standalone string, not a multiline comment.
+ Writing both "double" quotes and 'simple' quotes is fine as
+ long as there is only ""one"" or ''two'' of those in a row, not more.
+
+ If you have more quotes, they need to be escaped like this: \"\"\"
+ """
+ __ = '''
+ Another standalone string, this time with single quotes.
+ Writing both "double" quotes and 'simple' quotes is fine as
+ long as there is only ""one"" or ''two'' of those in a row, not more.
+
+ If you have more quotes, they need to be escaped like this: \'\'\'
+ '''
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_strings.out b/modules/gdscript/tests/scripts/parser/features/multiline_strings.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_strings.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd b/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd
new file mode 100644
index 0000000000..11a40fc00e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_vector.gd
@@ -0,0 +1,17 @@
+func test():
+ Vector2(
+ 1,
+ 2
+ )
+
+ Vector3(
+ 3,
+ 3.5,
+ 4, # Trailing comma should work.
+ )
+
+ Vector2i(1, 2,) # Trailing comma should work.
+
+ Vector3i(6,
+ 9,
+ 12)
diff --git a/modules/gdscript/tests/scripts/parser/features/multiline_vector.out b/modules/gdscript/tests/scripts/parser/features/multiline_vector.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/multiline_vector.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd
new file mode 100644
index 0000000000..b9bd19c9c5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.gd
@@ -0,0 +1,22 @@
+func test():
+ print(+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++2.718)
+
+ print()
+
+ print(------------------------------------------------------------------2.718)
+ print(-------------------------------------------------------------------2.718)
+
+ print()
+
+ print(~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~999)
+
+ print()
+
+ print(+-+-+-----+------------+++++++---++--++--+--+---+--++2.718)
+
+ print()
+
+ print(2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 2 / 0.444)
+ print(153023902390239 % 550 % 29 % 27 % 23 % 17)
+ print(2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 << 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2 >> 2)
+ print(8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8 ^ -8 ^ 8 ^ 8 ^ 8 ^ 8 ^ 8)
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out
new file mode 100644
index 0000000000..048cfbdfae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_arithmetic.out
@@ -0,0 +1,82 @@
+GDTEST_OK
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+>> WARNING
+>> Line: 19
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
+2.718
+
+2.718
+-2.718
+
+-1000
+
+-2.718
+
+36900.9009009009
+2
+8192
+-8
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_array.gd b/modules/gdscript/tests/scripts/parser/features/nested_array.gd
new file mode 100644
index 0000000000..3caef96391
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_array.gd
@@ -0,0 +1,5 @@
+func test():
+ var array = [[[[[[[[[[15]]]]]]]]]]
+ print(array[0][0][0][0][0][0][0][0])
+ print(array[0][0][0][0][0][0][0][0][0])
+ print(array[0][0][0][0][0][0][0][0][0][0])
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_array.out b/modules/gdscript/tests/scripts/parser/features/nested_array.out
new file mode 100644
index 0000000000..46c6ce3874
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_array.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+[[15]]
+[15]
+15
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd
new file mode 100644
index 0000000000..d67e142156
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.gd
@@ -0,0 +1,6 @@
+func test():
+ var dictionary = {1: {2: {3: {4: {5: {6: {7: {8: {"key": "value"}}}}}}}}}
+ print(dictionary[1][2][3][4][5][6][7])
+ print(dictionary[1][2][3][4][5][6][7][8])
+ print(dictionary[1][2][3][4][5][6][7][8].key)
+ print(dictionary[1][2][3][4][5][6][7][8]["key"])
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
new file mode 100644
index 0000000000..4009160439
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_dictionary.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+{8:{key:value}}
+{key:value}
+value
+value
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd
new file mode 100644
index 0000000000..59cdc7d6c2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.gd
@@ -0,0 +1,5 @@
+func foo(x):
+ return x + 1
+
+func test():
+ print(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(foo(0)))))))))))))))))))))))))
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_function_calls.out b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.out
new file mode 100644
index 0000000000..28a6636a7b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_function_calls.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+24
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_if.gd b/modules/gdscript/tests/scripts/parser/features/nested_if.gd
new file mode 100644
index 0000000000..7282d08497
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_if.gd
@@ -0,0 +1,57 @@
+func test():
+ # 20 levels of nesting (and then some).
+ if true:
+ print("1")
+ if true:
+ print("2")
+ if true:
+ print("3")
+ if true:
+ print("4")
+ if true:
+ print("5")
+ if true:
+ print("6")
+ if true:
+ print("7")
+ if true:
+ print("8")
+ if true:
+ print("9")
+ if true:
+ print("10")
+ if true:
+ print("11")
+ if true:
+ print("12")
+ if true:
+ print("13")
+ if true:
+ print("14")
+ if true:
+ print("15")
+ if true:
+ print("16")
+ if true:
+ print("17")
+ if true:
+ print("18")
+ if true:
+ print("19")
+ if true:
+ print("20")
+ if false:
+ print("End")
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ if true:
+ print("This won't be printed")
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_if.out b/modules/gdscript/tests/scripts/parser/features/nested_if.out
new file mode 100644
index 0000000000..c2d2e29a06
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_if.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.gd b/modules/gdscript/tests/scripts/parser/features/nested_match.gd
new file mode 100644
index 0000000000..aaddcc7e83
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_match.gd
@@ -0,0 +1,79 @@
+func test():
+ # 20 levels of nesting (and then some).
+ var number = 1234
+ match number:
+ 1234:
+ print("1")
+ match number:
+ 1234:
+ print("2")
+ match number:
+ 1234:
+ print("3")
+ continue
+ _:
+ print("Should also be printed")
+ match number:
+ 1234:
+ print("4")
+ match number:
+ _:
+ print("5")
+ match number:
+ false:
+ print("Should not be printed")
+ true:
+ print("Should not be printed")
+ "hello":
+ print("Should not be printed")
+ 1234:
+ print("6")
+ match number:
+ _:
+ print("7")
+ match number:
+ 1234:
+ print("8")
+ match number:
+ _:
+ print("9")
+ match number:
+ 1234:
+ print("10")
+ match number:
+ _:
+ print("11")
+ match number:
+ 1234:
+ print("12")
+ match number:
+ _:
+ print("13")
+ match number:
+ 1234:
+ print("14")
+ match number:
+ _:
+ print("15")
+ match number:
+ _:
+ print("16")
+ match number:
+ 1234:
+ print("17")
+ match number:
+ _:
+ print("18")
+ match number:
+ 1234:
+ print("19")
+ match number:
+ _:
+ print("20")
+ match number:
+ []:
+ print("Should not be printed")
+ _:
+ print("Should not be printed")
+ 5678:
+ print("Should not be printed either")
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_match.out b/modules/gdscript/tests/scripts/parser/features/nested_match.out
new file mode 100644
index 0000000000..651d76cc59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_match.out
@@ -0,0 +1,22 @@
+GDTEST_OK
+1
+2
+3
+Should also be printed
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd
new file mode 100644
index 0000000000..3fef73b9be
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.gd
@@ -0,0 +1,65 @@
+func test():
+ (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((print("Hello world!"))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
+
+ print(((((((((((((((((((((((((((((((((((((((((((((((((((((((((("Hello world 2!"))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
+
+ print(
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ (2)) + ((4))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))
diff --git a/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out
new file mode 100644
index 0000000000..27221a56bb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/nested_parentheses.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+Hello world!
+Hello world 2!
+6
diff --git a/modules/gdscript/tests/scripts/parser/features/number_separators.gd b/modules/gdscript/tests/scripts/parser/features/number_separators.gd
new file mode 100644
index 0000000000..f5f5661cae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/number_separators.gd
@@ -0,0 +1,12 @@
+func test():
+ # `_` can be used as a separator for numbers in GDScript.
+ # It can be placed anywhere in the number, except at the beginning.
+ # Currently, GDScript in the `master` branch only allows using one separator
+ # per number.
+ # Results are assigned to variables to avoid warnings.
+ var __ = 1_23
+ __ = 123_ # Trailing number separators are OK.
+ __ = 12_3
+ __ = 123_456
+ __ = 0x1234_5678
+ __ = 0b1001_0101
diff --git a/modules/gdscript/tests/scripts/parser/features/number_separators.out b/modules/gdscript/tests/scripts/parser/features/number_separators.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/number_separators.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/operator_assign.gd b/modules/gdscript/tests/scripts/parser/features/operator_assign.gd
new file mode 100644
index 0000000000..b5f07675ca
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/operator_assign.gd
@@ -0,0 +1,8 @@
+func test():
+ var i = 0
+ i += 5
+ i -= 4
+ i *= 10
+ i %= 8
+ i /= 0.25
+ print(round(i))
diff --git a/modules/gdscript/tests/scripts/parser/features/operator_assign.out b/modules/gdscript/tests/scripts/parser/features/operator_assign.out
new file mode 100644
index 0000000000..b0cb63ef59
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/operator_assign.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+8
diff --git a/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd
new file mode 100644
index 0000000000..9e4b360fb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.gd
@@ -0,0 +1,37 @@
+# 4.0+ replacement for `setget`:
+var _backing: int = 0
+var property:
+ get:
+ return _backing + 1000
+ set(value):
+ _backing = value - 1000
+
+
+func test():
+ print("Not using self:")
+ print(property)
+ print(_backing)
+ property = 5000
+ print(property)
+ print(_backing)
+ _backing = -50
+ print(property)
+ print(_backing)
+ property = 5000
+ print(property)
+ print(_backing)
+
+ # In Godot 4.0 and later, using `self` no longer makes a difference for
+ # getter/setter execution in GDScript.
+ print("Using self:")
+ print(self.property)
+ print(self._backing)
+ self.property = 5000
+ print(self.property)
+ print(self._backing)
+ self._backing = -50
+ print(self.property)
+ print(self._backing)
+ self.property = 5000
+ print(self.property)
+ print(self._backing)
diff --git a/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out
new file mode 100644
index 0000000000..560e0c3bd7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/property_setter_getter.out
@@ -0,0 +1,19 @@
+GDTEST_OK
+Not using self:
+1000
+0
+5000
+4000
+950
+-50
+5000
+4000
+Using self:
+5000
+4000
+5000
+4000
+950
+-50
+5000
+4000
diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
index 08f2eedb2d..d50776c25c 100644
--- a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
@@ -1,2 +1,5 @@
func test():
- print("A"); print("B")
+ print("A"); print("B")
+
+ # Multiple semicolons and whitespace between them is also valid.
+ print("A"); ;;;;; ; print("B");;
diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
index fc03f3efe8..bd7f38f516 100644
--- a/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
@@ -1,3 +1,5 @@
GDTEST_OK
A
B
+A
+B
diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.gd b/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.gd
new file mode 100644
index 0000000000..0f4aebb459
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.gd
@@ -0,0 +1,22 @@
+#GDTEST_OK
+
+func test():
+ a();
+ b();
+ c();
+ d();
+ e();
+
+func a(): print("a");
+
+func b(): print("b1"); print("b2")
+
+func c(): print("c1"); print("c2");
+
+func d():
+ print("d1");
+ print("d2")
+
+func e():
+ print("e1");
+ print("e2");
diff --git a/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.out b/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.out
new file mode 100644
index 0000000000..387cbd8fc2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_terminator.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+a
+b1
+b2
+c1
+c2
+d1
+d2
+e1
+e2
diff --git a/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd
new file mode 100644
index 0000000000..9ad98b78a8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/signal_declaration.gd
@@ -0,0 +1,20 @@
+#GDTEST_OK
+
+# No parentheses.
+signal a
+
+# No parameters.
+signal b()
+
+# With paramters.
+signal c(a, b, c)
+
+# With parameters multiline.
+signal d(
+ a,
+ b,
+ c,
+)
+
+func test():
+ print("Ok")
diff --git a/modules/gdscript/tests/scripts/parser/features/signal_declaration.out b/modules/gdscript/tests/scripts/parser/features/signal_declaration.out
new file mode 100644
index 0000000000..0e9f482af4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/signal_declaration.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Ok
diff --git a/modules/gdscript/tests/scripts/parser/features/single_line_declaration.gd b/modules/gdscript/tests/scripts/parser/features/single_line_declaration.gd
new file mode 100644
index 0000000000..650500663b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/single_line_declaration.gd
@@ -0,0 +1,11 @@
+#GDTEST_OK
+
+func test(): C.new().test("Ok"); test2()
+
+func test2(): print("Ok 2")
+
+class A: pass
+
+class B extends RefCounted: pass
+
+class C extends RefCounted: func test(x): print(x)
diff --git a/modules/gdscript/tests/scripts/parser/features/single_line_declaration.out b/modules/gdscript/tests/scripts/parser/features/single_line_declaration.out
new file mode 100644
index 0000000000..e021923dc2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/single_line_declaration.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Ok
+Ok 2
diff --git a/modules/gdscript/tests/scripts/parser/features/space_indentation.gd b/modules/gdscript/tests/scripts/parser/features/space_indentation.gd
new file mode 100644
index 0000000000..0a4887c199
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/space_indentation.gd
@@ -0,0 +1,4 @@
+func test():
+ # 2-space indentation should work, even though the GDScript style guide recommends tabs.
+ if true:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/features/space_indentation.out b/modules/gdscript/tests/scripts/parser/features/space_indentation.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/space_indentation.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.gd b/modules/gdscript/tests/scripts/parser/features/static_typing.gd
new file mode 100644
index 0000000000..d42632c82d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/static_typing.gd
@@ -0,0 +1,13 @@
+func test():
+ # The following lines are equivalent:
+ var _integer: int = 1
+ var _integer2 : int = 1
+ var _inferred := 1
+ var _inferred2 : = 1
+
+ # Type inference is automatic for constants.
+ const _INTEGER = 1
+ const _INTEGER_REDUNDANT_TYPED : int = 1
+ const _INTEGER_REDUNDANT_TYPED2 : int = 1
+ const _INTEGER_REDUNDANT_INFERRED := 1
+ const _INTEGER_REDUNDANT_INFERRED2 : = 1
diff --git a/modules/gdscript/tests/scripts/parser/features/static_typing.out b/modules/gdscript/tests/scripts/parser/features/static_typing.out
new file mode 100644
index 0000000000..92ce7bc0e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/static_typing.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+>> WARNING
+>> Line: 9
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INTEGER' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER'
+>> WARNING
+>> Line: 10
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INTEGER_REDUNDANT_TYPED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED'
+>> WARNING
+>> Line: 11
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INTEGER_REDUNDANT_TYPED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_TYPED2'
+>> WARNING
+>> Line: 12
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INTEGER_REDUNDANT_INFERRED' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED'
+>> WARNING
+>> Line: 13
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant '_INTEGER_REDUNDANT_INFERRED2' is declared but never used in the block. If this is intended, prefix it with an underscore: '__INTEGER_REDUNDANT_INFERRED2'
diff --git a/modules/gdscript/tests/scripts/parser/features/string_formatting.gd b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd
new file mode 100644
index 0000000000..a91837145d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd
@@ -0,0 +1,18 @@
+func test():
+ print("hello %s" % "world" == "hello world")
+ print("hello %s" % true == "hello true")
+ print("hello %s" % false == "hello false")
+
+ print("hello %d" % 25 == "hello 25")
+ print("hello %d %d" % [25, 42] == "hello 25 42")
+ # Pad with spaces.
+ print("hello %3d" % 25 == "hello 25")
+ # Pad with zeroes.
+ print("hello %03d" % 25 == "hello 025")
+
+ print("hello %.02f" % 0.123456 == "hello 0.12")
+
+ # Dynamic padding:
+ # <https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_format_string.html#dynamic-padding>
+ print("hello %*.*f" % [7, 3, 0.123456] == "hello 0.123")
+ print("hello %0*.*f" % [7, 3, 0.123456] == "hello 000.123")
diff --git a/modules/gdscript/tests/scripts/parser/features/string_formatting.out b/modules/gdscript/tests/scripts/parser/features/string_formatting.out
new file mode 100644
index 0000000000..7533f6ff54
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/string_formatting.out
@@ -0,0 +1,11 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
diff --git a/modules/gdscript/tests/scripts/parser/features/super.gd b/modules/gdscript/tests/scripts/parser/features/super.gd
new file mode 100644
index 0000000000..f5ae2a74a7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/super.gd
@@ -0,0 +1,60 @@
+class Say:
+ var prefix = "S"
+
+ func greet():
+ prefix = "S Greeted"
+ print("hello")
+
+ func say(name):
+ print(prefix, " say something ", name)
+
+
+class SayAnotherThing extends Say:
+ # This currently crashes the engine.
+ #var prefix = "SAT"
+
+ func greet():
+ prefix = "SAT Greeted"
+ print("hi")
+
+ func say(name):
+ print(prefix, " say another thing ", name)
+
+
+class SayNothing extends Say:
+ # This currently crashes the engine.
+ #var prefix = "SN"
+
+ func greet():
+ super()
+ prefix = "SN Greeted"
+ print("howdy, see above")
+
+ func greet_prefix_before_super():
+ prefix = "SN Greeted"
+ super.greet()
+ print("howdy, see above")
+
+ func say(name):
+ super(name + " super'd")
+ print(prefix, " say nothing... or not? ", name)
+
+
+func test():
+ var say = Say.new()
+ say.greet()
+ say.say("foo")
+ print()
+
+ var say_another_thing = SayAnotherThing.new()
+ say_another_thing.greet()
+ say_another_thing.say("bar")
+ print()
+
+ var say_nothing = SayNothing.new()
+ say_nothing.greet()
+ print(say_nothing.prefix)
+ say_nothing.greet_prefix_before_super()
+ print(say_nothing.prefix)
+ # This currently triggers a compiler bug: "compiler bug, function name not found"
+ #say_nothing.say("baz")
diff --git a/modules/gdscript/tests/scripts/parser/features/super.out b/modules/gdscript/tests/scripts/parser/features/super.out
new file mode 100644
index 0000000000..e0d4f4f098
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/super.out
@@ -0,0 +1,13 @@
+GDTEST_OK
+hello
+S Greeted say something foo
+
+hi
+SAT Greeted say another thing bar
+
+hello
+howdy, see above
+SN Greeted
+hello
+howdy, see above
+S Greeted
diff --git a/modules/gdscript/tests/scripts/parser/features/truthiness.gd b/modules/gdscript/tests/scripts/parser/features/truthiness.gd
new file mode 100644
index 0000000000..9c67a152f5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/truthiness.gd
@@ -0,0 +1,30 @@
+func test():
+ # The assertions below should all evaluate to `true` for this test to pass.
+ assert(true)
+ assert(not false)
+ assert(500)
+ assert(not 0)
+ assert(500.5)
+ assert(not 0.0)
+ assert("non-empty string")
+ assert(["non-empty array"])
+ assert({"non-empty": "dictionary"})
+ assert(Vector2(1, 0))
+ assert(Vector2i(-1, -1))
+ assert(Vector3(0, 0, 0.0001))
+ assert(Vector3i(0, 0, 10000))
+
+ # Zero position is `true` only if the Rect2's size is non-zero.
+ assert(Rect2(0, 0, 0, 1))
+
+ # Zero size is `true` only if the position is non-zero.
+ assert(Rect2(1, 1, 0, 0))
+
+ # Zero position is `true` only if the Rect2's size is non-zero.
+ assert(Rect2i(0, 0, 0, 1))
+
+ # Zero size is `true` only if the position is non-zero.
+ assert(Rect2i(1, 1, 0, 0))
+
+ # A fully black color is only truthy if its alpha component is not equal to `1`.
+ assert(Color(0, 0, 0, 0.5))
diff --git a/modules/gdscript/tests/scripts/parser/features/truthiness.out b/modules/gdscript/tests/scripts/parser/features/truthiness.out
new file mode 100644
index 0000000000..705524857b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/truthiness.out
@@ -0,0 +1,65 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 4
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 5
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 6
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 7
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 8
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 9
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 12
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 13
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 14
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 15
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 18
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 21
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 24
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 27
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 30
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
diff --git a/modules/gdscript/tests/scripts/parser/features/typed_arrays.gd b/modules/gdscript/tests/scripts/parser/features/typed_arrays.gd
new file mode 100644
index 0000000000..21bf3fdfcf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/typed_arrays.gd
@@ -0,0 +1,5 @@
+func test():
+ var my_array: Array[int] = [1, 2, 3]
+ var inferred_array := [1, 2, 3] # This is Array[int].
+ print(my_array)
+ print(inferred_array)
diff --git a/modules/gdscript/tests/scripts/parser/features/typed_arrays.out b/modules/gdscript/tests/scripts/parser/features/typed_arrays.out
new file mode 100644
index 0000000000..953d54d5e0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/typed_arrays.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+[1, 2, 3]
+[1, 2, 3]
diff --git a/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
index 3b48f10ca7..65013c4301 100644
--- a/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
@@ -1,12 +1,20 @@
-var a # No init.
-var b = 42 # Init.
+var m1 # No init.
+var m2 = 22 # Init.
+var m3: String # No init, typed.
+var m4: String = "44" # Init, typed.
+
func test():
- var c # No init, local.
- var d = 23 # Init, local.
+ var loc5 # No init, local.
+ var loc6 = 66 # Init, local.
+ var loc7: String # No init, typed.
+ var loc8: String = "88" # Init, typed.
+
+ m1 = 11
+ m3 = "33"
- a = 1
- c = 2
+ loc5 = 55
+ loc7 = "77"
- prints(a, b, c, d)
+ prints(m1, m2, m3, m4, loc5, loc6, loc7, loc8)
print("OK")
diff --git a/modules/gdscript/tests/scripts/parser/features/variable_declaration.out b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
index 2e0a63c024..7817dd3169 100644
--- a/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
@@ -1,7 +1,3 @@
GDTEST_OK
->> WARNING
->> Line: 5
->> UNASSIGNED_VARIABLE
->> The variable 'c' was used but never assigned a value.
-1 42 2 23
+11 22 33 44 55 66 77 88
OK
diff --git a/modules/gdscript/tests/scripts/parser/features/while.gd b/modules/gdscript/tests/scripts/parser/features/while.gd
new file mode 100644
index 0000000000..17dd4fbad2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/while.gd
@@ -0,0 +1,5 @@
+func test():
+ var i = 0
+ while i < 5:
+ print(i)
+ i += 1
diff --git a/modules/gdscript/tests/scripts/parser/features/while.out b/modules/gdscript/tests/scripts/parser/features/while.out
new file mode 100644
index 0000000000..b4a50885c7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/while.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+0
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd
new file mode 100644
index 0000000000..8feaed899f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.gd
@@ -0,0 +1,5 @@
+func test():
+ # These statements always evaluate to `true`, and therefore emit a warning.
+ assert(true)
+ assert(-1.234)
+ assert(2 + 3 == 5)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out
new file mode 100644
index 0000000000..5132792cb7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/assert_always_true.out
@@ -0,0 +1,13 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 4
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
+>> WARNING
+>> Line: 5
+>> ASSERT_ALWAYS_TRUE
+>> Assert statement is redundant because the expression is always true.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd
new file mode 100644
index 0000000000..f72b10213f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.gd
@@ -0,0 +1,8 @@
+func test():
+ # `and` should be used instead.
+ if true && true:
+ pass
+
+ # `or` should be used instead.
+ if false || true:
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/deprecated_operators.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.gd
@@ -0,0 +1 @@
+
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out
new file mode 100644
index 0000000000..20eec212ba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file.notest.out
@@ -0,0 +1,4 @@
+>> WARNING
+>> Line: 1
+>> EMPTY_FILE
+>> Empty script file.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd
new file mode 100644
index 0000000000..15cd95ff2b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.gd
@@ -0,0 +1 @@
+#a comment
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out
new file mode 100644
index 0000000000..20eec212ba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_comment.notest.out
@@ -0,0 +1,4 @@
+>> WARNING
+>> Line: 1
+>> EMPTY_FILE
+>> Empty script file.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd
new file mode 100644
index 0000000000..b28b04f643
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.gd
@@ -0,0 +1,3 @@
+
+
+
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out
new file mode 100644
index 0000000000..20eec212ba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline.notest.out
@@ -0,0 +1,4 @@
+>> WARNING
+>> Line: 1
+>> EMPTY_FILE
+>> Empty script file.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd
new file mode 100644
index 0000000000..ecdba44d21
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.gd
@@ -0,0 +1,4 @@
+#a comment, followed by a bunch of newlines
+
+
+
diff --git a/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out
new file mode 100644
index 0000000000..20eec212ba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment.notest.out
@@ -0,0 +1,4 @@
+>> WARNING
+>> Line: 1
+>> EMPTY_FILE
+>> Empty script file.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd
new file mode 100644
index 0000000000..a93ecb66b1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.gd
@@ -0,0 +1,8 @@
+func test():
+ # The ternary operator below returns values of different types and the
+ # result is assigned to a typed variable. This will cause a run-time error
+ # if the branch with the incompatible type is picked. Here, it won't happen
+ # since the `false` condition never evaluates to `true`. Instead, a warning
+ # will be emitted.
+ var __: int = 25
+ __ = "hello" if false else -2
diff --git a/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out
new file mode 100644
index 0000000000..7d1558c6fc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/incompatible_ternary.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 8
+>> INCOMPATIBLE_TERNARY
+>> Values of the ternary conditional are not mutually compatible.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd b/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd
new file mode 100644
index 0000000000..6117425528
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/integer_division.gd
@@ -0,0 +1,10 @@
+func test():
+ # This should emit a warning.
+ var __ = 5 / 2
+
+ # These should not emit warnings.
+ __ = float(5) / 2
+ __ = 5 / float(2)
+ __ = 5.0 / 2
+ __ = 5 / 2.0
+ __ = 5.0 / 2.0
diff --git a/modules/gdscript/tests/scripts/parser/warnings/integer_division.out b/modules/gdscript/tests/scripts/parser/warnings/integer_division.out
new file mode 100644
index 0000000000..40eb63ffcb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/integer_division.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> INTEGER_DIVISION
+>> Integer division, decimal part will be discarded.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd
new file mode 100644
index 0000000000..1eb54059dd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.gd
@@ -0,0 +1,9 @@
+func test():
+ var i = 25
+ # The default branch (`_`) should be at the end of the `match` statement.
+ # Otherwise, a warning will be emitted
+ match i:
+ _:
+ print("default")
+ 25:
+ print("is 25")
diff --git a/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out
new file mode 100644
index 0000000000..8630fab420
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/match_default_not_at_end.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 8
+>> UNREACHABLE_PATTERN
+>> Unreachable pattern (pattern after wildcard or bind).
+default
diff --git a/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd
new file mode 100644
index 0000000000..954e697145
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.gd
@@ -0,0 +1,5 @@
+func i_accept_ints_only(_i: int):
+ pass
+
+func test():
+ i_accept_ints_only(12.345)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out
new file mode 100644
index 0000000000..6fb592117b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/narrowing_conversion.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> NARROWING_CONVERSION
+>> Narrowing conversion (float is converted to int and loses precision).
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd
new file mode 100644
index 0000000000..00598e4d50
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.gd
@@ -0,0 +1,6 @@
+func i_return_int() -> int:
+ return 4
+
+
+func test():
+ i_return_int()
diff --git a/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/return_value_discarded.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd
new file mode 100644
index 0000000000..d565d38365
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.gd
@@ -0,0 +1,8 @@
+# See also `parser-errors/redefine-class-constant.gd`.
+const TEST = 25
+
+
+func test():
+ # Warning here. This is not an error because a new constant is created,
+ # rather than attempting to set the value of an existing constant.
+ const TEST = 50
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
new file mode 100644
index 0000000000..9c9417e11d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_constant.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+>> WARNING
+>> Line: 8
+>> UNUSED_LOCAL_CONSTANT
+>> The local constant 'TEST' is declared but never used in the block. If this is intended, prefix it with an underscore: '_TEST'
+>> WARNING
+>> Line: 8
+>> SHADOWED_VARIABLE
+>> The local constant "TEST" is shadowing an already-declared constant at line 2.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.gd
new file mode 100644
index 0000000000..3c64be571b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.gd
@@ -0,0 +1,2 @@
+func test():
+ var abs = "This variable has the same name as the built-in function."
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out
new file mode 100644
index 0000000000..c613140eb8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_VARIABLE
+>> The local variable 'abs' is declared but never used in the block. If this is intended, prefix it with an underscore: '_abs'
+>> WARNING
+>> Line: 2
+>> SHADOWED_GLOBAL_IDENTIFIER
+>> The variable 'abs' has the same name as a built-in function.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd
new file mode 100644
index 0000000000..66dcf309e8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.gd
@@ -0,0 +1,8 @@
+var foo = 123
+
+
+func test():
+ # Notice the `var` keyword. Without this keyword, no warning would be emitted
+ # because no new variable would be created. Instead, the class variable's value
+ # would be overwritten.
+ var foo = 456
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
new file mode 100644
index 0000000000..82e467b368
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_class.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+>> WARNING
+>> Line: 8
+>> UNUSED_VARIABLE
+>> The local variable 'foo' is declared but never used in the block. If this is intended, prefix it with an underscore: '_foo'
+>> WARNING
+>> Line: 8
+>> SHADOWED_VARIABLE
+>> The local variable "foo" is shadowing an already-declared variable at line 1.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd
new file mode 100644
index 0000000000..2c55d68be8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.gd
@@ -0,0 +1,2 @@
+func test():
+ var test = "This variable has the same name as the test() function."
diff --git a/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
new file mode 100644
index 0000000000..26ce0465b1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/shadowed_variable_function.out
@@ -0,0 +1,9 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_VARIABLE
+>> The local variable 'test' is declared but never used in the block. If this is intended, prefix it with an underscore: '_test'
+>> WARNING
+>> Line: 2
+>> SHADOWED_VARIABLE
+>> The local variable "test" is shadowing an already-declared function at line 1.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
new file mode 100644
index 0000000000..18ea260fa2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.gd
@@ -0,0 +1,9 @@
+func test():
+ # The following statements should all be reported as standalone expressions:
+ "This is a standalone expression"
+ 1234
+ 0.0 + 0.0
+ Color(1, 1, 1)
+ Vector3.ZERO
+ [true, false]
+ float(125)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
new file mode 100644
index 0000000000..99ec87438e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/standalone_expression.out
@@ -0,0 +1,21 @@
+GDTEST_OK
+>> WARNING
+>> Line: 3
+>> STANDALONE_EXPRESSION
+>> Standalone expression (the line has no effect).
+>> WARNING
+>> Line: 4
+>> STANDALONE_EXPRESSION
+>> Standalone expression (the line has no effect).
+>> WARNING
+>> Line: 5
+>> STANDALONE_EXPRESSION
+>> Standalone expression (the line has no effect).
+>> WARNING
+>> Line: 7
+>> STANDALONE_EXPRESSION
+>> Standalone expression (the line has no effect).
+>> WARNING
+>> Line: 8
+>> STANDALONE_EXPRESSION
+>> Standalone expression (the line has no effect).
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd
new file mode 100644
index 0000000000..afb5059eea
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.gd
@@ -0,0 +1,2 @@
+func test():
+ var __
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out
new file mode 100644
index 0000000000..cf14502e9a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNASSIGNED_VARIABLE
+>> The variable '__' was used but never assigned a value.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd
new file mode 100644
index 0000000000..d77791f4c5
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.gd
@@ -0,0 +1,4 @@
+func test():
+ var __: int
+ # Variable has no set value at this point (even though it's implicitly `0` here).
+ __ += 15
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out
new file mode 100644
index 0000000000..ba55a4e0f8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unassigned_variable_op_assign.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> UNASSIGNED_VARIABLE_OP_ASSIGN
+>> Using assignment with operation but the variable '__' was not previously assigned a value.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd
new file mode 100644
index 0000000000..3311f342ab
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.gd
@@ -0,0 +1,7 @@
+func test():
+ var i = 25
+
+ return
+
+ # This will never be run due to the `return` statement above.
+ print(i)
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out
new file mode 100644
index 0000000000..9316abd5eb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unreachable_code_after_return.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 7
+>> UNREACHABLE_CODE
+>> Unreachable code (statement after return) in function 'test()'.
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd
new file mode 100644
index 0000000000..e6e24dc6f2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.gd
@@ -0,0 +1,12 @@
+# This should emit a warning since the unused argument is not prefixed with an underscore.
+func function_with_unused_argument(p_arg1, p_arg2):
+ print(p_arg1)
+
+
+# This shouldn't emit a warning since the unused argument is prefixed with an underscore.
+func function_with_ignored_unused_argument(p_arg1, _p_arg2):
+ print(p_arg1)
+
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out
new file mode 100644
index 0000000000..92f3308f85
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_argument.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_PARAMETER
+>> The parameter 'p_arg2' is never used in the function 'function_with_unused_argument'. If this is intended, prefix it with an underscore: '_p_arg2'
diff --git a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
index 68e3bd424f..013a2e4beb 100644
--- a/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
@@ -1,2 +1,4 @@
func test():
- var unused = "not used"
+ var unused = "not used"
+
+ var _unused = "not used, but no warning since the variable name starts with an underscore"
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
new file mode 100644
index 0000000000..b4a42b3e3d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.gd
@@ -0,0 +1,6 @@
+func i_return_void() -> void:
+ return
+
+
+func test():
+ var __ = i_return_void()
diff --git a/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
new file mode 100644
index 0000000000..84c9598f9a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/void_assignment.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 6
+>> VOID_ASSIGNMENT
+>> Assignment operation, but the function 'i_return_void()' returns void.
diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd
new file mode 100644
index 0000000000..18174eae67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd
@@ -0,0 +1,32 @@
+# https://github.com/godotengine/godot/issues/48121
+
+func test():
+ var x := []
+ var y := []
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array()
+ y = Array()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array().duplicate()
+ y = Array().duplicate()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = [].duplicate()
+ y = [].duplicate()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array()
+ y = Array()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out
new file mode 100644
index 0000000000..f6b7d3cc39
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
new file mode 100644
index 0000000000..f6526aefb4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
@@ -0,0 +1,13 @@
+extends Node
+
+func test():
+ process_priority = 10
+ var change = 20
+
+ print(process_priority)
+ print(change)
+
+ process_priority += change
+
+ print(process_priority)
+ print(change)
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
new file mode 100644
index 0000000000..c9e6b34c77
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+10
+20
+30
+20
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd
new file mode 100644
index 0000000000..9da61ab184
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.gd
@@ -0,0 +1,8 @@
+# https://github.com/godotengine/godot/issues/50894
+
+func test():
+ print(await not_coroutine())
+
+
+func not_coroutine():
+ return "awaited"
diff --git a/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out
new file mode 100644
index 0000000000..c2ac488e9b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/await_without_coroutine.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+>> WARNING
+>> Line: 4
+>> REDUNDANT_AWAIT
+>> "await" keyword not needed in this case, because the expression isn't a coroutine nor a signal.
+awaited
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd
new file mode 100644
index 0000000000..c6645c2c34
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.gd
@@ -0,0 +1,138 @@
+func test():
+ var value
+
+ # null
+ value = null
+ print(value == null)
+
+ # bool
+ value = false
+ print(value == null)
+
+ # int
+ value = 0
+ print(value == null)
+
+ # float
+ value = 0.0
+ print(value == null)
+
+ # String
+ value = ""
+ print(value == null)
+
+ # Vector2
+ value = Vector2()
+ print(value == null)
+
+ # Vector2i
+ value = Vector2i()
+ print(value == null)
+
+ # Rect2
+ value = Rect2()
+ print(value == null)
+
+ # Rect2i
+ value = Rect2i()
+ print(value == null)
+
+ # Vector3
+ value = Vector3()
+ print(value == null)
+
+ # Vector3i
+ value = Vector3i()
+ print(value == null)
+
+ # Transform2D
+ value = Transform2D()
+ print(value == null)
+
+ # Plane
+ value = Plane()
+ print(value == null)
+
+ # Quaternion
+ value = Quaternion()
+ print(value == null)
+
+ # AABB
+ value = AABB()
+ print(value == null)
+
+ # Basis
+ value = Basis()
+ print(value == null)
+
+ # Transform3D
+ value = Transform3D()
+ print(value == null)
+
+ # Color
+ value = Color()
+ print(value == null)
+
+ # StringName
+ value = &""
+ print(value == null)
+
+ # NodePath
+ value = ^""
+ print(value == null)
+
+ # RID
+ value = RID()
+ print(value == null)
+
+ # Callable
+ value = Callable()
+ print(value == null)
+
+ # Signal
+ value = Signal()
+ print(value == null)
+
+ # Dictionary
+ value = {}
+ print(value == null)
+
+ # Array
+ value = []
+ print(value == null)
+
+ # PackedByteArray
+ value = PackedByteArray()
+ print(value == null)
+
+ # PackedInt32Array
+ value = PackedInt32Array()
+ print(value == null)
+
+ # PackedInt64Array
+ value = PackedInt64Array()
+ print(value == null)
+
+ # PackedFloat32Array
+ value = PackedFloat32Array()
+ print(value == null)
+
+ # PackedFloat64Array
+ value = PackedFloat64Array()
+ print(value == null)
+
+ # PackedStringArray
+ value = PackedStringArray()
+ print(value == null)
+
+ # PackedVector2Array
+ value = PackedVector2Array()
+ print(value == null)
+
+ # PackedVector3Array
+ value = PackedVector3Array()
+ print(value == null)
+
+ # PackedColorArray
+ value = PackedColorArray()
+ print(value == null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out
new file mode 100644
index 0000000000..639f6027b9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-equals-null.out
@@ -0,0 +1,35 @@
+GDTEST_OK
+true
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd
new file mode 100644
index 0000000000..ee622bf22f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.gd
@@ -0,0 +1,138 @@
+func test():
+ var value
+
+ # null
+ value = null
+ print(value != null)
+
+ # bool
+ value = false
+ print(value != null)
+
+ # int
+ value = 0
+ print(value != null)
+
+ # float
+ value = 0.0
+ print(value != null)
+
+ # String
+ value = ""
+ print(value != null)
+
+ # Vector2
+ value = Vector2()
+ print(value != null)
+
+ # Vector2i
+ value = Vector2i()
+ print(value != null)
+
+ # Rect2
+ value = Rect2()
+ print(value != null)
+
+ # Rect2i
+ value = Rect2i()
+ print(value != null)
+
+ # Vector3
+ value = Vector3()
+ print(value != null)
+
+ # Vector3i
+ value = Vector3i()
+ print(value != null)
+
+ # Transform2D
+ value = Transform2D()
+ print(value != null)
+
+ # Plane
+ value = Plane()
+ print(value != null)
+
+ # Quaternion
+ value = Quaternion()
+ print(value != null)
+
+ # AABB
+ value = AABB()
+ print(value != null)
+
+ # Basis
+ value = Basis()
+ print(value != null)
+
+ # Transform3D
+ value = Transform3D()
+ print(value != null)
+
+ # Color
+ value = Color()
+ print(value != null)
+
+ # StringName
+ value = &""
+ print(value != null)
+
+ # NodePath
+ value = ^""
+ print(value != null)
+
+ # RID
+ value = RID()
+ print(value != null)
+
+ # Callable
+ value = Callable()
+ print(value != null)
+
+ # Signal
+ value = Signal()
+ print(value != null)
+
+ # Dictionary
+ value = {}
+ print(value != null)
+
+ # Array
+ value = []
+ print(value != null)
+
+ # PackedByteArray
+ value = PackedByteArray()
+ print(value != null)
+
+ # PackedInt32Array
+ value = PackedInt32Array()
+ print(value != null)
+
+ # PackedInt64Array
+ value = PackedInt64Array()
+ print(value != null)
+
+ # PackedFloat32Array
+ value = PackedFloat32Array()
+ print(value != null)
+
+ # PackedFloat64Array
+ value = PackedFloat64Array()
+ print(value != null)
+
+ # PackedStringArray
+ value = PackedStringArray()
+ print(value != null)
+
+ # PackedVector2Array
+ value = PackedVector2Array()
+ print(value != null)
+
+ # PackedVector3Array
+ value = PackedVector3Array()
+ print(value != null)
+
+ # PackedColorArray
+ value = PackedColorArray()
+ print(value != null)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out
new file mode 100644
index 0000000000..d1e332afba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-builtin-not-equals-null.out
@@ -0,0 +1,35 @@
+GDTEST_OK
+false
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+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/runtime/features/compare-null-equals-builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd
new file mode 100644
index 0000000000..7649062fda
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.gd
@@ -0,0 +1,138 @@
+func test():
+ var value
+
+ # null
+ value = null
+ print(null == value)
+
+ # bool
+ value = false
+ print(null == value)
+
+ # int
+ value = 0
+ print(null == value)
+
+ # float
+ value = 0.0
+ print(null == value)
+
+ # String
+ value = ""
+ print(null == value)
+
+ # Vector2
+ value = Vector2()
+ print(null == value)
+
+ # Vector2i
+ value = Vector2i()
+ print(null == value)
+
+ # Rect2
+ value = Rect2()
+ print(null == value)
+
+ # Rect2i
+ value = Rect2i()
+ print(null == value)
+
+ # Vector3
+ value = Vector3()
+ print(null == value)
+
+ # Vector3i
+ value = Vector3i()
+ print(null == value)
+
+ # Transform2D
+ value = Transform2D()
+ print(null == value)
+
+ # Plane
+ value = Plane()
+ print(null == value)
+
+ # Quaternion
+ value = Quaternion()
+ print(null == value)
+
+ # AABB
+ value = AABB()
+ print(null == value)
+
+ # Basis
+ value = Basis()
+ print(null == value)
+
+ # Transform3D
+ value = Transform3D()
+ print(null == value)
+
+ # Color
+ value = Color()
+ print(null == value)
+
+ # StringName
+ value = &""
+ print(null == value)
+
+ # NodePath
+ value = ^""
+ print(null == value)
+
+ # RID
+ value = RID()
+ print(null == value)
+
+ # Callable
+ value = Callable()
+ print(null == value)
+
+ # Signal
+ value = Signal()
+ print(null == value)
+
+ # Dictionary
+ value = {}
+ print(null == value)
+
+ # Array
+ value = []
+ print(null == value)
+
+ # PackedByteArray
+ value = PackedByteArray()
+ print(null == value)
+
+ # PackedInt32Array
+ value = PackedInt32Array()
+ print(null == value)
+
+ # PackedInt64Array
+ value = PackedInt64Array()
+ print(null == value)
+
+ # PackedFloat32Array
+ value = PackedFloat32Array()
+ print(null == value)
+
+ # PackedFloat64Array
+ value = PackedFloat64Array()
+ print(null == value)
+
+ # PackedStringArray
+ value = PackedStringArray()
+ print(null == value)
+
+ # PackedVector2Array
+ value = PackedVector2Array()
+ print(null == value)
+
+ # PackedVector3Array
+ value = PackedVector3Array()
+ print(null == value)
+
+ # PackedColorArray
+ value = PackedColorArray()
+ print(null == value)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out
new file mode 100644
index 0000000000..639f6027b9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-equals-builtin.out
@@ -0,0 +1,35 @@
+GDTEST_OK
+true
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
+false
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd
new file mode 100644
index 0000000000..8d5f9df1b8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.gd
@@ -0,0 +1,138 @@
+func test():
+ var value
+
+ # null
+ value = null
+ print(null != value)
+
+ # bool
+ value = false
+ print(null != value)
+
+ # int
+ value = 0
+ print(null != value)
+
+ # float
+ value = 0.0
+ print(null != value)
+
+ # String
+ value = ""
+ print(null != value)
+
+ # Vector2
+ value = Vector2()
+ print(null != value)
+
+ # Vector2i
+ value = Vector2i()
+ print(null != value)
+
+ # Rect2
+ value = Rect2()
+ print(null != value)
+
+ # Rect2i
+ value = Rect2i()
+ print(null != value)
+
+ # Vector3
+ value = Vector3()
+ print(null != value)
+
+ # Vector3i
+ value = Vector3i()
+ print(null != value)
+
+ # Transform2D
+ value = Transform2D()
+ print(null != value)
+
+ # Plane
+ value = Plane()
+ print(null != value)
+
+ # Quaternion
+ value = Quaternion()
+ print(null != value)
+
+ # AABB
+ value = AABB()
+ print(null != value)
+
+ # Basis
+ value = Basis()
+ print(null != value)
+
+ # Transform3D
+ value = Transform3D()
+ print(null != value)
+
+ # Color
+ value = Color()
+ print(null != value)
+
+ # StringName
+ value = &""
+ print(null != value)
+
+ # NodePath
+ value = ^""
+ print(null != value)
+
+ # RID
+ value = RID()
+ print(null != value)
+
+ # Callable
+ value = Callable()
+ print(null != value)
+
+ # Signal
+ value = Signal()
+ print(null != value)
+
+ # Dictionary
+ value = {}
+ print(null != value)
+
+ # Array
+ value = []
+ print(null != value)
+
+ # PackedByteArray
+ value = PackedByteArray()
+ print(null != value)
+
+ # PackedInt32Array
+ value = PackedInt32Array()
+ print(null != value)
+
+ # PackedInt64Array
+ value = PackedInt64Array()
+ print(null != value)
+
+ # PackedFloat32Array
+ value = PackedFloat32Array()
+ print(null != value)
+
+ # PackedFloat64Array
+ value = PackedFloat64Array()
+ print(null != value)
+
+ # PackedStringArray
+ value = PackedStringArray()
+ print(null != value)
+
+ # PackedVector2Array
+ value = PackedVector2Array()
+ print(null != value)
+
+ # PackedVector3Array
+ value = PackedVector3Array()
+ print(null != value)
+
+ # PackedColorArray
+ value = PackedColorArray()
+ print(null != value)
diff --git a/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out
new file mode 100644
index 0000000000..d1e332afba
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/compare-null-not-equals-builtin.out
@@ -0,0 +1,35 @@
+GDTEST_OK
+false
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+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/runtime/features/dictionaries_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd
new file mode 100644
index 0000000000..d5a5f8de64
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd
@@ -0,0 +1,19 @@
+# https://github.com/godotengine/godot/issues/48121
+
+func test():
+ var x := Dictionary()
+ var y := Dictionary()
+ y[0]=1
+ y[1]=1
+ y[2]=1
+ print("TEST OTHER DICTIONARY: " + str(len(x)))
+ x.clear()
+
+ x = Dictionary().duplicate()
+ y = Dictionary().duplicate()
+ y[0]=1
+ y[1]=1
+ y[2]=1
+ print("TEST OTHER DICTIONARY: " + str(len(x)))
+ x.clear()
+ return
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out
new file mode 100644
index 0000000000..0bf49f5934
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+TEST OTHER DICTIONARY: 0
+TEST OTHER DICTIONARY: 0
diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd
new file mode 100644
index 0000000000..c9b5f8481e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd
@@ -0,0 +1,4 @@
+func test():
+ var dict = {}
+ dict.test = 1
+ print(dict.test)
diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.out b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out
new file mode 100644
index 0000000000..a7f1357bb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1
diff --git a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd
new file mode 100644
index 0000000000..3eb02816ed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd
@@ -0,0 +1,11 @@
+#GDTEST_OK
+var prop : int = 0:
+ get:
+ return prop
+ set(value):
+ prop = value % 7
+
+func test():
+ for i in 7:
+ prop += 1
+ print(prop)
diff --git a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out
new file mode 100644
index 0000000000..76157853f2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+1
+2
+3
+4
+5
+6
+0
diff --git a/modules/gdscript/tests/scripts/runtime/features/recursion.gd b/modules/gdscript/tests/scripts/runtime/features/recursion.gd
new file mode 100644
index 0000000000..a35485022e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/recursion.gd
@@ -0,0 +1,19 @@
+func is_prime(number: int, divisor: int = 2) -> bool:
+ print(divisor)
+ if number <= 2:
+ return (number == 2)
+ elif number % divisor == 0:
+ return false
+ elif divisor * divisor > number:
+ return true
+
+ return is_prime(number, divisor + 1)
+
+func test():
+ # Not a prime number.
+ print(is_prime(989))
+
+ print()
+
+ # Largest prime number below 10000.
+ print(is_prime(9973))
diff --git a/modules/gdscript/tests/scripts/runtime/features/recursion.out b/modules/gdscript/tests/scripts/runtime/features/recursion.out
new file mode 100644
index 0000000000..2bd8f24988
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/recursion.out
@@ -0,0 +1,125 @@
+GDTEST_OK
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+false
+
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+true
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
new file mode 100644
index 0000000000..fead2df854
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
@@ -0,0 +1,42 @@
+func test():
+ print(true, false)
+ print(-1, 0, 1)
+ print(-1.25, 0.25, 1.25)
+ print("hello world")
+
+ print(Vector2(0.25, 0.25))
+ print(Vector2i(0, 0))
+
+ print(Rect2(0.25, 0.25, 0.5, 0.5))
+ print(Rect2i(0, 0, 0, 0))
+
+ print(Vector3(0.25, 0.25, 0.25))
+ print(Vector3i(0, 0, 0))
+
+ print(Transform2D.IDENTITY)
+ print(Plane(1, 2, 3, 4))
+ print(Quaternion(1, 2, 3, 4))
+ print(AABB(Vector3.ZERO, Vector3.ONE))
+ print(Basis.from_euler(Vector3(0, 0, 0)))
+ print(Transform3D.IDENTITY)
+
+ print(Color(1, 2, 3, 4))
+ print(StringName("hello"))
+ print(NodePath("hello/world"))
+ var node := Node.new()
+ print(RID(node))
+ print(node.get_name)
+ print(node.property_list_changed)
+ node.free()
+ print({"hello":123})
+ print(["hello", 123])
+
+ print(PackedByteArray([-1, 0, 1]))
+ print(PackedInt32Array([-1, 0, 1]))
+ print(PackedInt64Array([-1, 0, 1]))
+ print(PackedFloat32Array([-1, 0, 1]))
+ print(PackedFloat64Array([-1, 0, 1]))
+ print(PackedStringArray(["hello", "world"]))
+ print(PackedVector2Array([Vector2.ONE, Vector2.ZERO]))
+ print(PackedVector3Array([Vector3.ONE, Vector3.ZERO]))
+ print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN]))
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out
new file mode 100644
index 0000000000..7670fc0128
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out
@@ -0,0 +1,34 @@
+GDTEST_OK
+truefalse
+-101
+-1.250.251.25
+hello world
+(0.25, 0.25)
+(0, 0)
+[P: (0.25, 0.25), S: (0.5, 0.5)]
+[P: (0, 0), S: (0, 0)]
+(0.25, 0.25, 0.25)
+(0, 0, 0)
+[X: (1, 0), Y: (0, 1), O: (0, 0)]
+[N: (1, 2, 3), D: 4]
+(1, 2, 3, 4)
+[P: (0, 0, 0), S: (1, 1, 1)]
+[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1)]
+[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)]
+(1, 2, 3, 4)
+hello
+hello/world
+RID(0)
+Node::get_name
+Node::[signal]property_list_changed
+{hello:123}
+[hello, 123]
+[255, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[hello, world]
+[(1, 1), (0, 0)]
+[(1, 1, 1), (0, 0, 0)]
+[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)]
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index e54f055f2b..80eabc1596 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -172,8 +172,8 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
return;
}
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
- const GDScriptFunction *func = E->value();
+ 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++) {
diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub
index 182272ffc7..1954a32697 100644
--- a/modules/glslang/SCsub
+++ b/modules/glslang/SCsub
@@ -13,46 +13,47 @@ if env["builtin_glslang"]:
thirdparty_dir = "#thirdparty/glslang/"
thirdparty_sources = [
"glslang/CInterface/glslang_c_interface.cpp",
- "glslang/MachineIndependent/RemoveTree.cpp",
- "glslang/MachineIndependent/ParseHelper.cpp",
- "glslang/MachineIndependent/iomapper.cpp",
- "glslang/MachineIndependent/propagateNoContraction.cpp",
- "glslang/MachineIndependent/Intermediate.cpp",
- "glslang/MachineIndependent/linkValidate.cpp",
"glslang/MachineIndependent/attribute.cpp",
- "glslang/MachineIndependent/Scan.cpp",
- "glslang/MachineIndependent/Initialize.cpp",
"glslang/MachineIndependent/Constant.cpp",
- "glslang/MachineIndependent/reflection.cpp",
+ "glslang/MachineIndependent/glslang_tab.cpp",
+ "glslang/MachineIndependent/InfoSink.cpp",
+ "glslang/MachineIndependent/Initialize.cpp",
+ "glslang/MachineIndependent/Intermediate.cpp",
+ "glslang/MachineIndependent/intermOut.cpp",
+ "glslang/MachineIndependent/IntermTraverse.cpp",
+ "glslang/MachineIndependent/iomapper.cpp",
"glslang/MachineIndependent/limits.cpp",
- "glslang/MachineIndependent/preprocessor/PpScanner.cpp",
- "glslang/MachineIndependent/preprocessor/PpTokens.cpp",
+ "glslang/MachineIndependent/linkValidate.cpp",
+ "glslang/MachineIndependent/parseConst.cpp",
+ "glslang/MachineIndependent/ParseContextBase.cpp",
+ "glslang/MachineIndependent/ParseHelper.cpp",
+ "glslang/MachineIndependent/PoolAlloc.cpp",
"glslang/MachineIndependent/preprocessor/PpAtom.cpp",
"glslang/MachineIndependent/preprocessor/PpContext.cpp",
"glslang/MachineIndependent/preprocessor/Pp.cpp",
- "glslang/MachineIndependent/InfoSink.cpp",
- "glslang/MachineIndependent/intermOut.cpp",
+ "glslang/MachineIndependent/preprocessor/PpScanner.cpp",
+ "glslang/MachineIndependent/preprocessor/PpTokens.cpp",
+ "glslang/MachineIndependent/propagateNoContraction.cpp",
+ "glslang/MachineIndependent/reflection.cpp",
+ "glslang/MachineIndependent/RemoveTree.cpp",
+ "glslang/MachineIndependent/Scan.cpp",
+ "glslang/MachineIndependent/ShaderLang.cpp",
+ "glslang/MachineIndependent/SpirvIntrinsics.cpp",
"glslang/MachineIndependent/SymbolTable.cpp",
- "glslang/MachineIndependent/glslang_tab.cpp",
"glslang/MachineIndependent/Versions.cpp",
- "glslang/MachineIndependent/ShaderLang.cpp",
- "glslang/MachineIndependent/parseConst.cpp",
- "glslang/MachineIndependent/PoolAlloc.cpp",
- "glslang/MachineIndependent/ParseContextBase.cpp",
- "glslang/MachineIndependent/IntermTraverse.cpp",
- "glslang/GenericCodeGen/Link.cpp",
"glslang/GenericCodeGen/CodeGen.cpp",
+ "glslang/GenericCodeGen/Link.cpp",
"OGLCompilersDLL/InitializeDll.cpp",
"SPIRV/CInterface/spirv_c_interface.cpp",
- "SPIRV/InReadableOrder.cpp",
- "SPIRV/GlslangToSpv.cpp",
- "SPIRV/SpvBuilder.cpp",
- "SPIRV/SpvTools.cpp",
"SPIRV/disassemble.cpp",
"SPIRV/doc.cpp",
- "SPIRV/SPVRemapper.cpp",
- "SPIRV/SpvPostProcess.cpp",
+ "SPIRV/GlslangToSpv.cpp",
+ "SPIRV/InReadableOrder.cpp",
"SPIRV/Logger.cpp",
+ "SPIRV/SpvBuilder.cpp",
+ "SPIRV/SpvPostProcess.cpp",
+ "SPIRV/SPVRemapper.cpp",
+ "SPIRV/SpvTools.cpp",
"StandAlone/ResourceLimits.cpp",
]
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index dd545ff431..69bf59f848 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -120,7 +120,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
preamble += "#define has_VK_KHR_multiview 1\n";
}
- if (preamble != "") {
+ if (!preamble.empty()) {
shader.setPreamble(preamble.c_str());
}
diff --git a/modules/gltf/config.py b/modules/gltf/config.py
index a4ee871eff..a4736321fa 100644
--- a/modules/gltf/config.py
+++ b/modules/gltf/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return env["tools"] and not env["disable_3d"]
+ return not env["disable_3d"]
def configure(env):
@@ -22,7 +22,8 @@ def get_doc_classes():
"GLTFSpecGloss",
"GLTFState",
"GLTFTexture",
- "PackedSceneGLTF",
+ "GLTFDocumentExtension",
+ "GLTFDocumentExtensionConvertImporterMesh",
]
diff --git a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml b/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml
deleted file mode 100644
index e717b30f73..0000000000
--- a/modules/gltf/doc_classes/EditorSceneImporterGLTF.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="EditorSceneImporterGLTF" inherits="EditorSceneImporter" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index 41a318ce19..ae81cae81a 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -6,8 +6,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="0">
</member>
@@ -38,6 +36,4 @@
<member name="type" type="int" setter="set_type" getter="get_type" default="0">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
index 5c1fa02f11..70480c2b38 100644
--- a/modules/gltf/doc_classes/GLTFAnimation.xml
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -6,12 +6,8 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="loop" type="bool" setter="set_loop" getter="get_loop" default="false">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
index edaad85e0a..f58aa46508 100644
--- a/modules/gltf/doc_classes/GLTFBufferView.xml
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -6,8 +6,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="buffer" type="int" setter="set_buffer" getter="get_buffer" default="-1">
</member>
@@ -20,6 +18,4 @@
<member name="indices" type="bool" setter="set_indices" getter="get_indices" default="false">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index 0b95f2c802..3682df5951 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -6,18 +6,14 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0">
+ <member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
</member>
- <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
+ <member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
</member>
- <member name="zfar" type="float" setter="set_zfar" getter="get_zfar" default="4000.0">
+ <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0">
</member>
- <member name="znear" type="float" setter="set_znear" getter="get_znear" default="0.05">
+ <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 04c40dd752..8d8e25e8b3 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -7,7 +7,31 @@
<tutorials>
</tutorials>
<methods>
+ <method name="import_scene">
+ <return type="Node" />
+ <argument index="0" name="path" type="String" />
+ <argument index="1" name="flags" type="int" default="0" />
+ <argument index="2" name="bake_fps" type="int" default="30" />
+ <argument index="3" name="state" type="GLTFState" default="null" />
+ <description>
+ Import a scene from glTF2 ".gltf" or ".glb" file.
+ </description>
+ </method>
+ <method name="save_scene">
+ <return type="int" enum="Error" />
+ <argument index="0" name="node" type="Node" />
+ <argument index="1" name="path" type="String" />
+ <argument index="2" name="src_path" type="String" />
+ <argument index="3" name="flags" type="int" default="0" />
+ <argument index="4" name="bake_fps" type="float" default="30" />
+ <argument index="5" name="state" type="GLTFState" default="null" />
+ <description>
+ Save a scene as a glTF2 ".glb" or ".gltf" file.
+ </description>
+ </method>
</methods>
- <constants>
- </constants>
+ <members>
+ <member name="extensions" type="GLTFDocumentExtension[]" setter="set_extensions" getter="get_extensions" default="[]">
+ </member>
+ </members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
new file mode 100644
index 0000000000..390bd3b30b
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFDocumentExtension" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="export_post">
+ <return type="int" enum="Error" />
+ <argument index="0" name="document" type="GLTFDocument" />
+ <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" />
+ <description>
+ </description>
+ </method>
+ <method name="get_export_setting" qualifiers="const">
+ <return type="Variant" />
+ <argument index="0" name="key" type="StringName" />
+ <description>
+ </description>
+ </method>
+ <method name="get_export_setting_keys" qualifiers="const">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_import_setting" qualifiers="const">
+ <return type="Variant" />
+ <argument index="0" name="key" type="StringName" />
+ <description>
+ </description>
+ </method>
+ <method name="get_import_setting_keys" qualifiers="const">
+ <return type="Array" />
+ <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" />
+ <description>
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/gdnative/doc_classes/PacketPeerGDNative.xml b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml
index ea9869cc58..452eec5f4f 100644
--- a/modules/gdnative/doc_classes/PacketPeerGDNative.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtensionConvertImporterMesh.xml
@@ -1,13 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="PacketPeerGDNative" inherits="PacketPeer" version="4.0">
+<class name="GLTFDocumentExtensionConvertImporterMesh" inherits="GLTFDocumentExtension" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml
index f51d287685..b4f03cd1ed 100644
--- a/modules/gltf/doc_classes/GLTFLight.xml
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -6,22 +6,26 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0, 0, 0, 1)">
+ <member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)">
+ The [Color] of the light. Defaults to white. A black color causes the light to have no effect.
</member>
<member name="inner_cone_angle" type="float" setter="set_inner_cone_angle" getter="get_inner_cone_angle" default="0.0">
+ The inner angle of the cone in a spotlight. Must be less than or equal to the outer cone angle.
+ Within this angle, the light is at full brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. When creating a Godot [SpotLight3D], the ratio between the inner and outer cone angles is used to calculate the attenuation of the light.
</member>
- <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="0.0">
+ <member name="intensity" type="float" setter="set_intensity" getter="get_intensity" default="1.0">
+ The intensity of the light. This is expressed in candelas (lumens per steradian) for point and spot lights, and lux (lumens per m²) for directional lights. When creating a Godot light, this value is converted to a unitless multiplier.
</member>
- <member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.0">
+ <member name="light_type" type="String" setter="set_light_type" getter="get_light_type" default="&quot;&quot;">
+ The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively.
</member>
- <member name="range" type="float" setter="set_range" getter="get_range" default="0.0">
+ <member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.785398">
+ The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle.
+ At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
</member>
- <member name="type" type="String" setter="set_type" getter="get_type" default="&quot;&quot;">
+ <member name="range" type="float" setter="set_range" getter="get_range" default="inf">
+ The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index fd7e4a169e..58853217e2 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -6,14 +6,12 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()">
</member>
- <member name="mesh" type="EditorSceneImporterMesh" setter="set_mesh" getter="get_mesh">
+ <member name="instance_materials" type="Array" setter="set_instance_materials" getter="get_instance_materials" default="[]">
+ </member>
+ <member name="mesh" type="ImporterMesh" setter="set_mesh" getter="get_mesh">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index bfbb12df4d..f27965ea07 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -6,8 +6,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
</member>
@@ -23,6 +21,8 @@
</member>
<member name="parent" type="int" setter="set_parent" getter="get_parent" default="-1">
</member>
+ <member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
+ </member>
<member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)">
</member>
<member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)">
@@ -31,11 +31,7 @@
</member>
<member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
</member>
- <member name="translation" type="Vector3" setter="set_translation" getter="get_translation" default="Vector3(0, 0, 0)">
- </member>
<member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
index 6e83cec252..037c3545a6 100644
--- a/modules/gltf/doc_classes/GLTFSkeleton.xml
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -52,6 +52,4 @@
<member name="roots" type="PackedInt32Array" setter="set_roots" getter="get_roots" default="PackedInt32Array()">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml
index 107ca960cd..ad4f017584 100644
--- a/modules/gltf/doc_classes/GLTFSkin.xml
+++ b/modules/gltf/doc_classes/GLTFSkin.xml
@@ -57,6 +57,4 @@
<member name="skin_root" type="int" setter="set_skin_root" getter="get_skin_root" default="-1">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
index 6e9c419649..6b8f86ed1c 100644
--- a/modules/gltf/doc_classes/GLTFSpecGloss.xml
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -6,8 +6,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)">
</member>
@@ -20,6 +18,4 @@
<member name="specular_factor" type="Color" setter="set_specular_factor" getter="get_specular_factor" default="Color(1, 1, 1, 1)">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index ae976fc04c..6d03d0ecf8 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -209,6 +209,4 @@
<member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index 33bd8fddeb..7c88d2318e 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -6,12 +6,8 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="src_image" type="int" setter="set_src_image" getter="get_src_image" default="0">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gltf/doc_classes/PackedSceneGLTF.xml b/modules/gltf/doc_classes/PackedSceneGLTF.xml
deleted file mode 100644
index d0136c6402..0000000000
--- a/modules/gltf/doc_classes/PackedSceneGLTF.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="PackedSceneGLTF" inherits="PackedScene" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="export_gltf">
- <return type="int" enum="Error" />
- <argument index="0" name="node" type="Node" />
- <argument index="1" name="path" type="String" />
- <argument index="2" name="flags" type="int" default="0" />
- <argument index="3" name="bake_fps" type="float" default="1000.0" />
- <description>
- </description>
- </method>
- <method name="import_gltf_scene">
- <return type="Node" />
- <argument index="0" name="path" type="String" />
- <argument index="1" name="flags" type="int" default="0" />
- <argument index="2" name="bake_fps" type="float" default="1000.0" />
- <argument index="3" name="state" type="GLTFState" default="null" />
- <description>
- </description>
- </method>
- <method name="pack_gltf">
- <return type="void" />
- <argument index="0" name="path" type="String" />
- <argument index="1" name="flags" type="int" default="0" />
- <argument index="2" name="bake_fps" type="float" default="1000.0" />
- <argument index="3" name="state" type="GLTFState" default="null" />
- <description>
- </description>
- </method>
- </methods>
- <members>
- <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" override="true" default="{&quot;conn_count&quot;: 0,&quot;conns&quot;: PackedInt32Array(),&quot;editable_instances&quot;: [],&quot;names&quot;: PackedStringArray(),&quot;node_count&quot;: 0,&quot;node_paths&quot;: [],&quot;nodes&quot;: PackedInt32Array(),&quot;variants&quot;: [],&quot;version&quot;: 2}" />
- </members>
- <constants>
- </constants>
-</class>
diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
index ae080bcc9a..25fda7ef3b 100644
--- a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
@@ -28,11 +28,14 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#if TOOLS_ENABLED
#include "editor_scene_exporter_gltf_plugin.h"
#include "core/config/project_settings.h"
+#include "core/error/error_list.h"
#include "core/object/object.h"
#include "core/templates/vector.h"
#include "editor/editor_file_system.h"
+#include "gltf_document.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/gui/check_box.h"
#include "scene/main/node.h"
@@ -49,7 +52,6 @@ bool SceneExporterGLTFPlugin::has_main_screen() const {
SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) {
editor = p_node;
- convert_gltf2.instantiate();
file_export_lib = memnew(EditorFileDialog);
editor->get_gui_base()->add_child(file_export_lib);
file_export_lib->connect("file_selected", callable_mp(this, &SceneExporterGLTFPlugin::_gltf2_dialog_action));
@@ -71,8 +73,12 @@ void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
return;
}
List<String> deps;
- convert_gltf2->save_scene(root, p_file, p_file, 0, 1000.0f, &deps);
- EditorFileSystem::get_singleton()->scan_changes();
+ Ref<GLTFDocument> doc;
+ doc.instantiate();
+ Error err = doc->save_scene(root, p_file, p_file, 0, 30.0f, Ref<GLTFState>());
+ if (err != OK) {
+ ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
+ }
}
void SceneExporterGLTFPlugin::convert_scene_to_gltf2() {
@@ -81,10 +87,12 @@ void SceneExporterGLTFPlugin::convert_scene_to_gltf2() {
editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
return;
}
- String filename = String(root->get_filename().get_file().get_basename());
+ String filename = String(root->get_scene_file_path().get_file().get_basename());
if (filename.is_empty()) {
filename = root->get_name();
}
file_export_lib->set_current_file(filename + String(".gltf"));
file_export_lib->popup_centered_ratio();
}
+
+#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h
index d952894c16..89a8e27053 100644
--- a/modules/gltf/editor_scene_exporter_gltf_plugin.h
+++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h
@@ -31,13 +31,14 @@
#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
+#if TOOLS_ENABLED
#include "editor/editor_plugin.h"
+
#include "editor_scene_importer_gltf.h"
class SceneExporterGLTFPlugin : public EditorPlugin {
GDCLASS(SceneExporterGLTFPlugin, EditorPlugin);
- Ref<PackedSceneGLTF> convert_gltf2;
EditorNode *editor = nullptr;
EditorFileDialog *file_export_lib = nullptr;
void _gltf2_dialog_action(String p_file);
@@ -48,5 +49,5 @@ public:
bool has_main_screen() const override;
SceneExporterGLTFPlugin(class EditorNode *p_node);
};
-
+#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
diff --git a/modules/gltf/editor_scene_importer_gltf.cpp b/modules/gltf/editor_scene_importer_gltf.cpp
index eca1c85bf3..1a172877a0 100644
--- a/modules/gltf/editor_scene_importer_gltf.cpp
+++ b/modules/gltf/editor_scene_importer_gltf.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#if TOOLS_ENABLED
#include "editor_scene_importer_gltf.h"
#include "gltf_document.h"
@@ -37,137 +38,28 @@
#include "scene/animation/animation_player.h"
#include "scene/resources/animation.h"
-uint32_t EditorSceneImporterGLTF::get_import_flags() const {
+uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
}
-void EditorSceneImporterGLTF::get_extensions(List<String> *r_extensions) const {
+void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) const {
r_extensions->push_back("gltf");
r_extensions->push_back("glb");
}
-Node *EditorSceneImporterGLTF::import_scene(const String &p_path,
+Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path,
uint32_t p_flags, int p_bake_fps,
List<String> *r_missing_deps,
Error *r_err) {
- Ref<PackedSceneGLTF> importer;
- importer.instantiate();
- return importer->import_scene(p_path, p_flags, p_bake_fps, r_missing_deps, r_err, Ref<GLTFState>());
+ Ref<GLTFDocument> doc;
+ doc.instantiate();
+ return doc->import_scene_gltf(p_path, p_flags, p_bake_fps, Ref<GLTFState>(), r_missing_deps, r_err);
}
-Ref<Animation> EditorSceneImporterGLTF::import_animation(const String &p_path,
+Ref<Animation> EditorSceneFormatImporterGLTF::import_animation(const String &p_path,
uint32_t p_flags,
int p_bake_fps) {
return Ref<Animation>();
}
-void PackedSceneGLTF::_bind_methods() {
- ClassDB::bind_method(
- D_METHOD("export_gltf", "node", "path", "flags", "bake_fps"),
- &PackedSceneGLTF::export_gltf, DEFVAL(0), DEFVAL(1000.0f));
- ClassDB::bind_method(D_METHOD("pack_gltf", "path", "flags", "bake_fps", "state"),
- &PackedSceneGLTF::pack_gltf, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>()));
- ClassDB::bind_method(D_METHOD("import_gltf_scene", "path", "flags", "bake_fps", "state"),
- &PackedSceneGLTF::import_gltf_scene, DEFVAL(0), DEFVAL(1000.0f), DEFVAL(Ref<GLTFState>()));
-}
-Node *PackedSceneGLTF::import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state) {
- Error err = FAILED;
- List<String> deps;
- return import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state);
-}
-
-Node *PackedSceneGLTF::import_scene(const String &p_path, uint32_t p_flags,
- int p_bake_fps,
- List<String> *r_missing_deps,
- Error *r_err,
- Ref<GLTFState> r_state) {
- if (r_state == Ref<GLTFState>()) {
- r_state.instantiate();
- }
- r_state->use_named_skin_binds =
- p_flags & EditorSceneImporter::IMPORT_USE_NAMED_SKIN_BINDS;
-
- Ref<GLTFDocument> gltf_document;
- gltf_document.instantiate();
- Error err = gltf_document->parse(r_state, p_path);
- if (r_err) {
- *r_err = err;
- }
- ERR_FAIL_COND_V(err != Error::OK, nullptr);
-
- Node3D *root = memnew(Node3D);
- for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) {
- gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]);
- }
- gltf_document->_process_mesh_instances(r_state, root);
- if (r_state->animations.size()) {
- AnimationPlayer *ap = memnew(AnimationPlayer);
- root->add_child(ap);
- ap->set_owner(root);
- for (int i = 0; i < r_state->animations.size(); i++) {
- gltf_document->_import_animation(r_state, ap, i, p_bake_fps);
- }
- }
-
- return cast_to<Node3D>(root);
-}
-
-void PackedSceneGLTF::pack_gltf(String p_path, int32_t p_flags,
- real_t p_bake_fps, Ref<GLTFState> r_state) {
- Error err = FAILED;
- List<String> deps;
- Node *root = import_scene(p_path, p_flags, p_bake_fps, &deps, &err, r_state);
- ERR_FAIL_COND(err != OK);
- pack(root);
-}
-
-void PackedSceneGLTF::save_scene(Node *p_node, const String &p_path,
- const String &p_src_path, uint32_t p_flags,
- int p_bake_fps, List<String> *r_missing_deps,
- Error *r_err) {
- Error err = FAILED;
- if (r_err) {
- *r_err = err;
- }
- Ref<GLTFDocument> gltf_document;
- gltf_document.instantiate();
- Ref<GLTFState> state;
- state.instantiate();
- err = gltf_document->serialize(state, p_node, p_path);
- if (r_err) {
- *r_err = err;
- }
-}
-
-void PackedSceneGLTF::_build_parent_hierachy(Ref<GLTFState> state) {
- // build the hierarchy
- for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
- for (int j = 0; j < state->nodes[node_i]->children.size(); j++) {
- GLTFNodeIndex child_i = state->nodes[node_i]->children[j];
- ERR_FAIL_INDEX(child_i, state->nodes.size());
- if (state->nodes.write[child_i]->parent != -1) {
- continue;
- }
- state->nodes.write[child_i]->parent = node_i;
- }
- }
-}
-
-Error PackedSceneGLTF::export_gltf(Node *p_root, String p_path,
- int32_t p_flags,
- real_t p_bake_fps) {
- ERR_FAIL_COND_V(!p_root, FAILED);
- List<String> deps;
- Error err;
- String path = p_path;
- int32_t flags = p_flags;
- real_t baked_fps = p_bake_fps;
- Ref<PackedSceneGLTF> exporter;
- exporter.instantiate();
- exporter->save_scene(p_root, path, "", flags, baked_fps, &deps, &err);
- int32_t error_code = err;
- if (error_code != 0) {
- return Error(error_code);
- }
- return OK;
-}
+#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h
index 7bc5f594ed..28963adc28 100644
--- a/modules/gltf/editor_scene_importer_gltf.h
+++ b/modules/gltf/editor_scene_importer_gltf.h
@@ -30,51 +30,26 @@
#ifndef EDITOR_SCENE_IMPORTER_GLTF_H
#define EDITOR_SCENE_IMPORTER_GLTF_H
-
+#ifdef TOOLS_ENABLED
#include "gltf_state.h"
+#include "gltf_document_extension.h"
+
#include "editor/import/resource_importer_scene.h"
#include "scene/main/node.h"
#include "scene/resources/packed_scene.h"
class Animation;
-#ifdef TOOLS_ENABLED
-class EditorSceneImporterGLTF : public EditorSceneImporter {
- GDCLASS(EditorSceneImporterGLTF, EditorSceneImporter);
+class EditorSceneFormatImporterGLTF : public EditorSceneFormatImporter {
+ GDCLASS(EditorSceneFormatImporterGLTF, EditorSceneFormatImporter);
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,
- int p_bake_fps,
- List<String> *r_missing_deps = nullptr,
- Error *r_err = nullptr) override;
+ virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual Ref<Animation> import_animation(const String &p_path,
uint32_t p_flags, int p_bake_fps) override;
};
-#endif
-
-class PackedSceneGLTF : public PackedScene {
- GDCLASS(PackedSceneGLTF, PackedScene);
-
-protected:
- static void _bind_methods();
-
-public:
- virtual void save_scene(Node *p_node, const String &p_path, const String &p_src_path,
- uint32_t p_flags, int p_bake_fps,
- List<String> *r_missing_deps, Error *r_err = nullptr);
- virtual void _build_parent_hierachy(Ref<GLTFState> state);
- virtual Error export_gltf(Node *p_root, String p_path, int32_t p_flags = 0,
- real_t p_bake_fps = 1000.0f);
- virtual Node *import_scene(const String &p_path, uint32_t p_flags,
- int p_bake_fps,
- List<String> *r_missing_deps,
- Error *r_err,
- Ref<GLTFState> r_state);
- virtual Node *import_gltf_scene(const String &p_path, uint32_t p_flags, float p_bake_fps, Ref<GLTFState> r_state = Ref<GLTFState>());
- virtual void pack_gltf(String p_path, int32_t p_flags = 0,
- real_t p_bake_fps = 1000.0f, Ref<GLTFState> r_state = Ref<GLTFState>());
-};
+#endif // TOOLS_ENABLED
#endif // EDITOR_SCENE_IMPORTER_GLTF_H
diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/gltf_accessor.cpp
index daeb084916..85cec3fec4 100644
--- a/modules/gltf/gltf_accessor.cpp
+++ b/modules/gltf/gltf_accessor.cpp
@@ -30,6 +30,8 @@
#include "gltf_accessor.h"
+#include "gltf_document_extension.h"
+
void GLTFAccessor::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view);
ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view);
diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h
index 949a601730..bec511f974 100644
--- a/modules/gltf/gltf_accessor.h
+++ b/modules/gltf/gltf_accessor.h
@@ -32,7 +32,9 @@
#define GLTF_ACCESSOR_H
#include "core/io/resource.h"
+
#include "gltf_document.h"
+#include "gltf_document_extension.h"
struct GLTFAccessor : public Resource {
GDCLASS(GLTFAccessor, Resource);
@@ -44,8 +46,7 @@ private:
int component_type = 0;
bool normalized = false;
int count = 0;
- GLTFDocument::GLTFType
- type = GLTFDocument::TYPE_SCALAR;
+ GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
Vector<double> min;
Vector<double> max;
int sparse_count = 0;
diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/gltf_animation.h
index 216d2161c4..763494cb8d 100644
--- a/modules/gltf/gltf_animation.h
+++ b/modules/gltf/gltf_animation.h
@@ -50,15 +50,15 @@ public:
template <class T>
struct Channel {
Interpolation interpolation;
- Vector<float> times;
+ Vector<real_t> times;
Vector<T> values;
};
struct Track {
- Channel<Vector3> translation_track;
+ Channel<Vector3> position_track;
Channel<Quaternion> rotation_track;
Channel<Vector3> scale_track;
- Vector<Channel<float>> weight_tracks;
+ Vector<Channel<real_t>> weight_tracks;
};
public:
diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/gltf_buffer_view.cpp
index ba38a11c4c..d00e0f040f 100644
--- a/modules/gltf/gltf_buffer_view.cpp
+++ b/modules/gltf/gltf_buffer_view.cpp
@@ -30,6 +30,8 @@
#include "gltf_buffer_view.h"
+#include "gltf_document_extension.h"
+
void GLTFBufferView::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_buffer"), &GLTFBufferView::get_buffer);
ClassDB::bind_method(D_METHOD("set_buffer", "buffer"), &GLTFBufferView::set_buffer);
diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/gltf_camera.cpp
index efa7c5d6d7..0f895fb989 100644
--- a/modules/gltf/gltf_camera.cpp
+++ b/modules/gltf/gltf_camera.cpp
@@ -35,13 +35,13 @@ void GLTFCamera::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_perspective", "perspective"), &GLTFCamera::set_perspective);
ClassDB::bind_method(D_METHOD("get_fov_size"), &GLTFCamera::get_fov_size);
ClassDB::bind_method(D_METHOD("set_fov_size", "fov_size"), &GLTFCamera::set_fov_size);
- ClassDB::bind_method(D_METHOD("get_zfar"), &GLTFCamera::get_zfar);
- ClassDB::bind_method(D_METHOD("set_zfar", "zfar"), &GLTFCamera::set_zfar);
- ClassDB::bind_method(D_METHOD("get_znear"), &GLTFCamera::get_znear);
- ClassDB::bind_method(D_METHOD("set_znear", "znear"), &GLTFCamera::set_znear);
+ ClassDB::bind_method(D_METHOD("get_depth_far"), &GLTFCamera::get_depth_far);
+ ClassDB::bind_method(D_METHOD("set_depth_far", "zdepth_far"), &GLTFCamera::set_depth_far);
+ ClassDB::bind_method(D_METHOD("get_depth_near"), &GLTFCamera::get_depth_near);
+ ClassDB::bind_method(D_METHOD("set_depth_near", "zdepth_near"), &GLTFCamera::set_depth_near);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "perspective"), "set_perspective", "get_perspective"); // bool
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov_size"), "set_fov_size", "get_fov_size"); // float
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zfar"), "set_zfar", "get_zfar"); // float
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "znear"), "set_znear", "get_znear"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_far"), "set_depth_far", "get_depth_far"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near"); // float
}
diff --git a/modules/gltf/gltf_camera.h b/modules/gltf/gltf_camera.h
index bf94b80bef..843ff417a4 100644
--- a/modules/gltf/gltf_camera.h
+++ b/modules/gltf/gltf_camera.h
@@ -39,8 +39,8 @@ class GLTFCamera : public Resource {
private:
bool perspective = true;
float fov_size = 75.0;
- float zfar = 4000.0;
- float znear = 0.05;
+ float depth_far = 4000.0;
+ float depth_near = 0.05;
protected:
static void _bind_methods();
@@ -50,9 +50,9 @@ public:
void set_perspective(bool p_val) { perspective = p_val; }
float get_fov_size() const { return fov_size; }
void set_fov_size(float p_val) { fov_size = p_val; }
- float get_zfar() const { return zfar; }
- void set_zfar(float p_val) { zfar = p_val; }
- float get_znear() const { return znear; }
- void set_znear(float p_val) { znear = p_val; }
+ float get_depth_far() const { return depth_far; }
+ void set_depth_far(float p_val) { depth_far = p_val; }
+ float get_depth_near() const { return depth_near; }
+ void set_depth_near(float p_val) { depth_near = p_val; }
};
#endif // GLTF_CAMERA_H
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index ff0579a11c..1c1ee5cd10 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -33,6 +33,8 @@
#include "gltf_accessor.h"
#include "gltf_animation.h"
#include "gltf_camera.h"
+#include "gltf_document_extension.h"
+#include "gltf_document_extension_convert_importer_mesh.h"
#include "gltf_light.h"
#include "gltf_mesh.h"
#include "gltf_node.h"
@@ -48,6 +50,8 @@
#include "core/io/file_access.h"
#include "core/io/json.h"
#include "core/math/disjoint_set.h"
+#include "core/math/vector2.h"
+#include "core/variant/dictionary.h"
#include "core/variant/typed_array.h"
#include "core/variant/variant.h"
#include "core/version.h"
@@ -56,11 +60,16 @@
#include "editor/import/resource_importer_scene.h"
#include "scene/2d/node_2d.h"
#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/multimesh_instance_3d.h"
#include "scene/animation/animation_player.h"
+#include "scene/resources/importer_mesh.h"
+#include "scene/resources/mesh.h"
+#include "scene/resources/multimesh.h"
#include "scene/resources/surface_tool.h"
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
#ifdef MODULE_CSG_ENABLED
#include "modules/csg/csg_shape.h"
#endif // MODULE_CSG_ENABLED
@@ -70,12 +79,16 @@
#include <stdio.h>
#include <stdlib.h>
+#include <cstdint>
#include <limits>
Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) {
uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
- _convert_scene_node(state, p_root, p_root, -1, -1);
+ state->skeleton3d_to_gltf_skeleton.clear();
+ state->skin_and_skeleton3d_to_gltf_skin.clear();
+
+ _convert_scene_node(state, p_root, -1, -1);
if (!state->buffers.size()) {
state->buffers.push_back(Vector<uint8_t>());
}
@@ -94,11 +107,7 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
if (err != OK) {
return Error::FAILED;
}
- /* STEP 4 CREATE BONE ATTACHMENTS */
- err = _serialize_bone_attachment(state);
- if (err != OK) {
- return Error::FAILED;
- }
+
/* STEP 5 SERIALIZE MESHES (we have enough info now) */
err = _serialize_meshes(state);
if (err != OK) {
@@ -111,26 +120,26 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
return Error::FAILED;
}
- /* STEP 7 SERIALIZE IMAGES */
- err = _serialize_images(state, p_path);
+ /* STEP 7 SERIALIZE ANIMATIONS */
+ err = _serialize_animations(state);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 8 SERIALIZE TEXTURES */
- err = _serialize_textures(state);
+ /* STEP 8 SERIALIZE ACCESSORS */
+ err = _encode_accessors(state);
if (err != OK) {
return Error::FAILED;
}
- // /* STEP 9 SERIALIZE ANIMATIONS */
- err = _serialize_animations(state);
+ /* STEP 9 SERIALIZE IMAGES */
+ err = _serialize_images(state, p_path);
if (err != OK) {
return Error::FAILED;
}
- /* STEP 10 SERIALIZE ACCESSORS */
- err = _encode_accessors(state);
+ /* STEP 10 SERIALIZE TEXTURES */
+ err = _serialize_textures(state);
if (err != OK) {
return Error::FAILED;
}
@@ -183,7 +192,7 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
uint64_t elapsed = OS::get_singleton()->get_ticks_usec() - begin_time;
float elapsed_sec = double(elapsed) / 1000000.0;
elapsed_sec = Math::snapped(elapsed_sec, 0.01f);
- print_line("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2));
+ print_verbose("glTF: Export time elapsed seconds " + rtos(elapsed_sec).pad_decimals(2));
return OK;
}
@@ -238,7 +247,7 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
JSON json;
err = json.parse(text);
if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
return err;
}
state->json = json.get_data();
@@ -246,30 +255,6 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
return OK;
}
-Error GLTFDocument::_serialize_bone_attachment(Ref<GLTFState> state) {
- for (int skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
- for (int attachment_i = 0; attachment_i < state->skeletons[skeleton_i]->bone_attachments.size(); attachment_i++) {
- BoneAttachment3D *bone_attachment = state->skeletons[skeleton_i]->bone_attachments[attachment_i];
- String bone_name = bone_attachment->get_bone_name();
- bone_name = _sanitize_bone_name(bone_name);
- int32_t bone = state->skeletons[skeleton_i]->godot_skeleton->find_bone(bone_name);
- ERR_CONTINUE(bone == -1);
- for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) {
- if (state->skins[skin_i]->skeleton != skeleton_i) {
- continue;
- }
-
- for (int node_i = 0; node_i < bone_attachment->get_child_count(); node_i++) {
- ERR_CONTINUE(bone >= state->skins[skin_i]->joints.size());
- _convert_scene_node(state, bone_attachment->get_child(node_i), bone_attachment->get_owner(), state->skins[skin_i]->joints[bone], 0);
- }
- break;
- }
- }
- }
- return OK;
-}
-
Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
Error err;
FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -297,7 +282,7 @@ Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
JSON json;
err = json.parse(text);
if (err != OK) {
- _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error("", p_path.utf8().get_data(), json.get_error_line(), json.get_error_message().utf8().get_data(), false, ERR_HANDLER_SCRIPT);
return err;
}
@@ -426,8 +411,8 @@ Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
node["scale"] = _vec3_to_arr(n->scale);
}
- if (!n->translation.is_equal_approx(Vector3())) {
- node["translation"] = _vec3_to_arr(n->translation);
+ if (!n->position.is_equal_approx(Vector3())) {
+ node["translation"] = _vec3_to_arr(n->position);
}
if (n->children.size()) {
Array children;
@@ -581,7 +566,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
node->xform = _arr_to_xform(n["matrix"]);
} else {
if (n.has("translation")) {
- node->translation = _arr_to_vec3(n["translation"]);
+ node->position = _arr_to_vec3(n["translation"]);
}
if (n.has("rotation")) {
node->rotation = _arr_to_quaternion(n["rotation"]);
@@ -591,7 +576,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
}
node->xform.basis.set_quaternion_scale(node->rotation, node->scale);
- node->xform.origin = node->translation;
+ node->xform.origin = node->position;
}
if (n.has("extensions")) {
@@ -810,6 +795,9 @@ Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) {
buffers.push_back(d);
}
print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size()));
+ if (!buffers.size()) {
+ return OK;
+ }
state->json["bufferViews"] = buffers;
return OK;
}
@@ -899,6 +887,9 @@ Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) {
accessors.push_back(d);
}
+ if (!accessors.size()) {
+ return OK;
+ }
state->json["accessors"] = accessors;
ERR_FAIL_COND_V(!state->json.has("accessors"), ERR_FILE_CORRUPT);
print_verbose("glTF: Total accessors: " + itos(state->accessors.size()));
@@ -2123,16 +2114,20 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
Array meshes;
for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < state->meshes.size(); gltf_mesh_i++) {
print_verbose("glTF: Serializing mesh: " + itos(gltf_mesh_i));
- Ref<EditorSceneImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh();
+ Ref<ImporterMesh> import_mesh = state->meshes.write[gltf_mesh_i]->get_mesh();
if (import_mesh.is_null()) {
continue;
}
+ Array instance_materials = state->meshes.write[gltf_mesh_i]->get_instance_materials();
Array primitives;
- Array targets;
Dictionary gltf_mesh;
Array target_names;
Array weights;
+ for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) {
+ target_names.push_back(import_mesh->get_blend_shape_name(morph_i));
+ }
for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) {
+ Array targets;
Dictionary primitive;
Mesh::PrimitiveType primitive_type = import_mesh->get_surface_primitive_type(surface_i);
switch (primitive_type) {
@@ -2170,11 +2165,14 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
}
Array array = import_mesh->get_surface_arrays(surface_i);
+ uint32_t format = import_mesh->get_surface_format(surface_i);
+ int32_t vertex_num = 0;
Dictionary attributes;
{
Vector<Vector3> a = array[Mesh::ARRAY_VERTEX];
ERR_FAIL_COND_V(!a.size(), ERR_INVALID_DATA);
attributes["POSITION"] = _encode_accessor_as_vec3(state, a, true);
+ vertex_num = a.size();
}
{
Vector<real_t> a = array[Mesh::ARRAY_TANGENT];
@@ -2217,6 +2215,58 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
attributes["TEXCOORD_1"] = _encode_accessor_as_vec2(state, a, true);
}
}
+ for (int custom_i = 0; custom_i < 3; custom_i++) {
+ Vector<float> a = array[Mesh::ARRAY_CUSTOM0 + custom_i];
+ if (a.size()) {
+ int num_channels = 4;
+ int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS;
+ switch ((format >> custom_shift) & Mesh::ARRAY_FORMAT_CUSTOM_MASK) {
+ case Mesh::ARRAY_CUSTOM_R_FLOAT:
+ num_channels = 1;
+ break;
+ case Mesh::ARRAY_CUSTOM_RG_FLOAT:
+ num_channels = 2;
+ break;
+ case Mesh::ARRAY_CUSTOM_RGB_FLOAT:
+ num_channels = 3;
+ break;
+ case Mesh::ARRAY_CUSTOM_RGBA_FLOAT:
+ num_channels = 4;
+ break;
+ }
+ int texcoord_i = 2 + 2 * custom_i;
+ String gltf_texcoord_key;
+ for (int prev_texcoord_i = 0; prev_texcoord_i < texcoord_i; prev_texcoord_i++) {
+ gltf_texcoord_key = vformat("TEXCOORD_%d", prev_texcoord_i);
+ if (!attributes.has(gltf_texcoord_key)) {
+ Vector<Vector2> empty;
+ empty.resize(vertex_num);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, empty, true);
+ }
+ }
+
+ LocalVector<Vector2> first_channel;
+ first_channel.resize(vertex_num);
+ LocalVector<Vector2> second_channel;
+ second_channel.resize(vertex_num);
+ for (int32_t vert_i = 0; vert_i < vertex_num; vert_i++) {
+ float u = a[vert_i * num_channels + 0];
+ float v = (num_channels == 1 ? 0.0f : a[vert_i * num_channels + 1]);
+ first_channel[vert_i] = Vector2(u, v);
+ u = 0;
+ v = 0;
+ if (num_channels >= 3) {
+ u = a[vert_i * num_channels + 2];
+ v = (num_channels == 3 ? 0.0f : a[vert_i * num_channels + 3]);
+ second_channel[vert_i] = Vector2(u, v);
+ }
+ }
+ gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, first_channel, true);
+ gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
+ attributes[gltf_texcoord_key] = _encode_accessor_as_vec2(state, second_channel, true);
+ }
+ }
{
Vector<Color> a = array[Mesh::ARRAY_COLOR];
if (a.size()) {
@@ -2252,13 +2302,12 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
}
attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true);
} else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) {
- int32_t vertex_count = vertex_array.size();
Vector<Color> joints_0;
- joints_0.resize(vertex_count);
+ joints_0.resize(vertex_num);
Vector<Color> joints_1;
- joints_1.resize(vertex_count);
+ joints_1.resize(vertex_num);
int32_t weights_8_count = JOINT_GROUP_SIZE * 2;
- for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) {
+ for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) {
Color joint_0;
joint_0.r = a[vertex_i * weights_8_count + 0];
joint_0.g = a[vertex_i * weights_8_count + 1];
@@ -2280,21 +2329,20 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
const Array &a = array[Mesh::ARRAY_WEIGHTS];
const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX];
if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) {
- const int ret_size = a.size() / JOINT_GROUP_SIZE;
+ int32_t vertex_count = vertex_array.size();
Vector<Color> attribs;
- attribs.resize(ret_size);
- for (int i = 0; i < ret_size; i++) {
+ attribs.resize(vertex_count);
+ for (int i = 0; i < vertex_count; i++) {
attribs.write[i] = Color(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]);
}
attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true);
} else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) {
- int32_t vertex_count = vertex_array.size();
Vector<Color> weights_0;
- weights_0.resize(vertex_count);
+ weights_0.resize(vertex_num);
Vector<Color> weights_1;
- weights_1.resize(vertex_count);
+ weights_1.resize(vertex_num);
int32_t weights_8_count = JOINT_GROUP_SIZE * 2;
- for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) {
+ for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) {
Color weight_0;
weight_0.r = a[vertex_i * weights_8_count + 0];
weight_0.g = a[vertex_i * weights_8_count + 1];
@@ -2354,7 +2402,6 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode();
for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) {
Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i);
- target_names.push_back(import_mesh->get_blend_shape_name(morph_i));
Dictionary t;
Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
Array mesh_arrays = import_mesh->get_surface_arrays(surface_i);
@@ -2371,28 +2418,34 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
}
Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
- if (varr.size()) {
+ if (narr.size()) {
t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true);
}
Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
if (tarr.size()) {
const int ret_size = tarr.size() / 4;
- Vector<Color> attribs;
+ Vector<Vector3> attribs;
attribs.resize(ret_size);
for (int i = 0; i < ret_size; i++) {
- Color tangent;
- tangent.r = tarr[(i * 4) + 0];
- tangent.g = tarr[(i * 4) + 1];
- tangent.b = tarr[(i * 4) + 2];
- tangent.a = tarr[(i * 4) + 3];
+ Vector3 vec3;
+ vec3.x = tarr[(i * 4) + 0];
+ vec3.y = tarr[(i * 4) + 1];
+ vec3.z = tarr[(i * 4) + 2];
}
- t["TANGENT"] = _encode_accessor_as_color(state, attribs, true);
+ t["TANGENT"] = _encode_accessor_as_vec3(state, attribs, true);
}
targets.push_back(t);
}
}
- Ref<BaseMaterial3D> mat = import_mesh->get_surface_material(surface_i);
+ Variant v;
+ if (surface_i < instance_materials.size()) {
+ v = instance_materials.get(surface_i);
+ }
+ Ref<BaseMaterial3D> mat = v;
+ if (!mat.is_valid()) {
+ 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) {
@@ -2415,12 +2468,13 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
Dictionary e;
e["targetNames"] = target_names;
- for (int j = 0; j < target_names.size(); j++) {
+ weights.resize(target_names.size());
+ for (int name_i = 0; name_i < target_names.size(); name_i++) {
real_t weight = 0.0;
- if (j < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
- weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[j];
+ if (name_i < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
+ weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[name_i];
}
- weights.push_back(weight);
+ weights[name_i] = weight;
}
if (weights.size()) {
gltf_mesh["weights"] = weights;
@@ -2435,6 +2489,9 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
meshes.push_back(gltf_mesh);
}
+ if (!meshes.size()) {
+ return OK;
+ }
state->json["meshes"] = meshes;
print_verbose("glTF: Total meshes: " + itos(meshes.size()));
@@ -2459,7 +2516,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
Array primitives = d["primitives"];
const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
- Ref<EditorSceneImporterMesh> import_mesh;
+ Ref<ImporterMesh> import_mesh;
import_mesh.instantiate();
String mesh_name = "mesh";
if (d.has("name") && !String(d["name"]).is_empty()) {
@@ -2468,6 +2525,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
import_mesh->set_name(_gen_unique_name(state, vformat("%s_%s", state->scene_name, mesh_name)));
for (int j = 0; j < primitives.size(); j++) {
+ uint32_t flags = 0;
Dictionary p = primitives[j];
Array array;
@@ -2499,8 +2557,11 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR);
+ int32_t vertex_num = 0;
if (a.has("POSITION")) {
- array[Mesh::ARRAY_VERTEX] = _decode_accessor_as_vec3(state, a["POSITION"], true);
+ PackedVector3Array vertices = _decode_accessor_as_vec3(state, a["POSITION"], true);
+ array[Mesh::ARRAY_VERTEX] = vertices;
+ vertex_num = vertices.size();
}
if (a.has("NORMAL")) {
array[Mesh::ARRAY_NORMAL] = _decode_accessor_as_vec3(state, a["NORMAL"], true);
@@ -2514,6 +2575,60 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
if (a.has("TEXCOORD_1")) {
array[Mesh::ARRAY_TEX_UV2] = _decode_accessor_as_vec2(state, a["TEXCOORD_1"], true);
}
+ for (int custom_i = 0; custom_i < 3; custom_i++) {
+ Vector<float> cur_custom;
+ Vector<Vector2> texcoord_first;
+ Vector<Vector2> texcoord_second;
+
+ int texcoord_i = 2 + 2 * custom_i;
+ String gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i);
+ int num_channels = 0;
+ if (a.has(gltf_texcoord_key)) {
+ texcoord_first = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true);
+ num_channels = 2;
+ }
+ gltf_texcoord_key = vformat("TEXCOORD_%d", texcoord_i + 1);
+ if (a.has(gltf_texcoord_key)) {
+ texcoord_second = _decode_accessor_as_vec2(state, a[gltf_texcoord_key], true);
+ num_channels = 4;
+ }
+ if (!num_channels) {
+ break;
+ }
+ if (num_channels == 2 || num_channels == 4) {
+ cur_custom.resize(vertex_num * num_channels);
+ for (int32_t uv_i = 0; uv_i < texcoord_first.size() && uv_i < vertex_num; uv_i++) {
+ cur_custom.write[uv_i * num_channels + 0] = texcoord_first[uv_i].x;
+ cur_custom.write[uv_i * num_channels + 1] = texcoord_first[uv_i].y;
+ }
+ // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0:
+ for (int32_t uv_i = texcoord_first.size(); uv_i < vertex_num; uv_i++) {
+ cur_custom.write[uv_i * num_channels + 0] = 0;
+ cur_custom.write[uv_i * num_channels + 1] = 0;
+ }
+ }
+ if (num_channels == 4) {
+ for (int32_t uv_i = 0; uv_i < texcoord_second.size() && uv_i < vertex_num; uv_i++) {
+ // num_channels must be 4
+ cur_custom.write[uv_i * num_channels + 2] = texcoord_second[uv_i].x;
+ cur_custom.write[uv_i * num_channels + 3] = texcoord_second[uv_i].y;
+ }
+ // Vector.resize seems to not zero-initialize. Ensure all unused elements are 0:
+ for (int32_t uv_i = texcoord_second.size(); uv_i < vertex_num; uv_i++) {
+ cur_custom.write[uv_i * num_channels + 2] = 0;
+ cur_custom.write[uv_i * num_channels + 3] = 0;
+ }
+ }
+ if (cur_custom.size() > 0) {
+ array[Mesh::ARRAY_CUSTOM0 + custom_i] = cur_custom;
+ int custom_shift = Mesh::ARRAY_FORMAT_CUSTOM0_SHIFT + custom_i * Mesh::ARRAY_FORMAT_CUSTOM_BITS;
+ if (num_channels == 2) {
+ flags |= Mesh::ARRAY_CUSTOM_RG_FLOAT << custom_shift;
+ } else {
+ flags |= Mesh::ARRAY_CUSTOM_RGBA_FLOAT << custom_shift;
+ }
+ }
+ }
if (a.has("COLOR_0")) {
array[Mesh::ARRAY_COLOR] = _decode_accessor_as_color(state, a["COLOR_0"], true);
has_vertex_color = true;
@@ -2525,10 +2640,9 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
PackedInt32Array joints_1 = _decode_accessor_as_ints(state, a["JOINTS_1"], true);
ERR_FAIL_COND_V(joints_0.size() != joints_0.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
- int32_t vertex_count = joints_0.size() / JOINT_GROUP_SIZE;
Vector<int> joints;
- joints.resize(vertex_count * weight_8_count);
- for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) {
+ joints.resize(vertex_num * weight_8_count);
+ for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) {
joints.write[vertex_i * weight_8_count + 0] = joints_0[vertex_i * JOINT_GROUP_SIZE + 0];
joints.write[vertex_i * weight_8_count + 1] = joints_0[vertex_i * JOINT_GROUP_SIZE + 1];
joints.write[vertex_i * weight_8_count + 2] = joints_0[vertex_i * JOINT_GROUP_SIZE + 2];
@@ -2567,9 +2681,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
Vector<float> weights;
ERR_FAIL_COND_V(weights_0.size() != weights_1.size(), ERR_INVALID_DATA);
int32_t weight_8_count = JOINT_GROUP_SIZE * 2;
- int32_t vertex_count = weights_0.size() / JOINT_GROUP_SIZE;
- weights.resize(vertex_count * weight_8_count);
- for (int32_t vertex_i = 0; vertex_i < vertex_count; vertex_i++) {
+ weights.resize(vertex_num * weight_8_count);
+ for (int32_t vertex_i = 0; vertex_i < vertex_num; vertex_i++) {
weights.write[vertex_i * weight_8_count + 0] = weights_0[vertex_i * JOINT_GROUP_SIZE + 0];
weights.write[vertex_i * weight_8_count + 1] = weights_0[vertex_i * JOINT_GROUP_SIZE + 1];
weights.write[vertex_i * weight_8_count + 2] = weights_0[vertex_i * JOINT_GROUP_SIZE + 2];
@@ -2642,17 +2755,18 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
bool generate_tangents = (primitive == Mesh::PRIMITIVE_TRIANGLES && !a.has("TANGENT") && a.has("TEXCOORD_0") && a.has("NORMAL"));
+ Ref<SurfaceTool> mesh_surface_tool;
+ mesh_surface_tool.instantiate();
+ mesh_surface_tool->create_from_triangle_arrays(array);
+ if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
+ mesh_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS);
+ }
+ mesh_surface_tool->index();
if (generate_tangents) {
//must generate mikktspace tangents.. ergh..
- Ref<SurfaceTool> st;
- st.instantiate();
- st->create_from_triangle_arrays(array);
- if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
- st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS);
- }
- st->generate_tangents();
- array = st->commit_to_arrays();
+ mesh_surface_tool->generate_tangents();
}
+ array = mesh_surface_tool->commit_to_arrays();
Array morphs;
//blend shapes
@@ -2682,8 +2796,6 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
array_copy[l] = array[l];
}
- array_copy[Mesh::ARRAY_INDEX] = Variant();
-
if (t.has("POSITION")) {
Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true);
const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
@@ -2762,17 +2874,17 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
array_copy[Mesh::ARRAY_TANGENT] = tangents_v4;
}
+ Ref<SurfaceTool> blend_surface_tool;
+ blend_surface_tool.instantiate();
+ blend_surface_tool->create_from_triangle_arrays(array_copy);
+ if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
+ blend_surface_tool->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS);
+ }
+ blend_surface_tool->index();
if (generate_tangents) {
- Ref<SurfaceTool> st;
- st.instantiate();
- st->create_from_triangle_arrays(array_copy);
- if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
- st->set_skin_weight_count(SurfaceTool::SKIN_8_WEIGHTS);
- }
- st->deindex();
- st->generate_tangents();
- array_copy = st->commit_to_arrays();
+ blend_surface_tool->generate_tangents();
}
+ array_copy = blend_surface_tool->commit_to_arrays();
morphs.push_back(array_copy);
}
@@ -2785,19 +2897,23 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
const int material = p["material"];
ERR_FAIL_INDEX_V(material, state->materials.size(), ERR_FILE_CORRUPT);
Ref<BaseMaterial3D> mat3d = state->materials[material];
+ ERR_FAIL_NULL_V(mat3d, ERR_FILE_CORRUPT);
if (has_vertex_color) {
mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
}
mat = mat3d;
- } else if (has_vertex_color) {
+ } else {
Ref<StandardMaterial3D> mat3d;
mat3d.instantiate();
- mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ if (has_vertex_color) {
+ mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ }
mat = mat3d;
}
-
- import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat.is_valid() ? mat->get_name() : String());
+ ERR_FAIL_NULL_V(mat, ERR_FILE_CORRUPT);
+ import_mesh->add_surface(primitive, array, morphs,
+ Dictionary(), mat, mat->get_name(), flags);
}
Vector<float> blend_weights;
@@ -2953,6 +3069,7 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_pat
}
}
} else { // Relative path to an external image file.
+ uri = uri.uri_decode();
uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows.
// ResourceLoader will rely on the file extension to use the relevant loader.
// The spec says that if mimeType is defined, it should take precedence (e.g.
@@ -3354,6 +3471,9 @@ Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
}
materials.push_back(d);
}
+ if (!materials.size()) {
+ return OK;
+ }
state->json["materials"] = materials;
print_verbose("Total materials: " + itos(state->materials.size()));
@@ -3519,7 +3639,6 @@ Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
}
}
-
if (d.has("alphaMode")) {
const String &am = d["alphaMode"];
if (am == "BLEND") {
@@ -3643,10 +3762,7 @@ void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor
r_base_color.g = Math::lerp(base_color_from_diffuse.g, base_color_from_specular.g, r_metallic * r_metallic);
r_base_color.b = Math::lerp(base_color_from_diffuse.b, base_color_from_specular.b, r_metallic * r_metallic);
r_base_color.a = p_diffuse.a;
- r_base_color.r = CLAMP(r_base_color.r, 0.0f, 1.0f);
- r_base_color.g = CLAMP(r_base_color.g, 0.0f, 1.0f);
- r_base_color.b = CLAMP(r_base_color.b, 0.0f, 1.0f);
- r_base_color.a = CLAMP(r_base_color.a, 0.0f, 1.0f);
+ r_base_color = r_base_color.clamp();
}
GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vector<GLTFNodeIndex> &subset) {
@@ -4174,6 +4290,7 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
Skeleton3D *skeleton = memnew(Skeleton3D);
gltf_skeleton->godot_skeleton = skeleton;
+ state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i;
// Make a unique name, no gltf node represents this skeleton
skeleton->set_name(_gen_unique_name(state, "Skeleton3D"));
@@ -4221,6 +4338,9 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
skeleton->add_bone(node->get_name());
skeleton->set_bone_rest(bone_index, node->xform);
+ skeleton->set_bone_pose_position(bone_index, node->position);
+ skeleton->set_bone_pose_rotation(bone_index, node->rotation.normalized());
+ skeleton->set_bone_pose_scale(bone_index, node->scale);
if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) {
const int bone_parent = skeleton->find_bone(state->nodes[node->parent]->get_name());
@@ -4259,6 +4379,20 @@ Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFSt
Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) {
_remove_duplicate_skins(state);
+ Array json_skins;
+ for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) {
+ Ref<GLTFSkin> gltf_skin = state->skins[skin_i];
+ Dictionary json_skin;
+ json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false);
+ json_skin["joints"] = gltf_skin->get_joints();
+ json_skin["name"] = gltf_skin->get_name();
+ json_skins.push_back(json_skin);
+ }
+ if (!state->skins.size()) {
+ return OK;
+ }
+
+ state->json["skins"] = json_skins;
return OK;
}
@@ -4356,8 +4490,8 @@ Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) {
color[1] = light->color.g;
color[2] = light->color.b;
d["color"] = color;
- d["type"] = light->type;
- if (light->type == "spot") {
+ d["type"] = light->light_type;
+ if (light->light_type == "spot") {
Dictionary s;
float inner_cone_angle = light->inner_cone_angle;
s["innerConeAngle"] = inner_cone_angle;
@@ -4403,16 +4537,16 @@ Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) {
Dictionary og;
og["ymag"] = Math::deg2rad(camera->get_fov_size());
og["xmag"] = Math::deg2rad(camera->get_fov_size());
- og["zfar"] = camera->get_zfar();
- og["znear"] = camera->get_znear();
+ og["zfar"] = camera->get_depth_far();
+ og["znear"] = camera->get_depth_near();
d["orthographic"] = og;
d["type"] = "orthographic";
} else if (camera->get_perspective()) {
Dictionary ppt;
// GLTF spec is in radians, Godot's camera is in degrees.
ppt["yfov"] = Math::deg2rad(camera->get_fov_size());
- ppt["zfar"] = camera->get_zfar();
- ppt["znear"] = camera->get_znear();
+ ppt["zfar"] = camera->get_depth_far();
+ ppt["znear"] = camera->get_depth_near();
d["perspective"] = ppt;
d["type"] = "perspective";
}
@@ -4452,7 +4586,7 @@ Error GLTFDocument::_parse_lights(Ref<GLTFState> state) {
light.instantiate();
ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
const String &type = d["type"];
- light->type = type;
+ light->light_type = type;
if (d.has("color")) {
const Array &arr = d["color"];
@@ -4472,7 +4606,7 @@ Error GLTFDocument::_parse_lights(Ref<GLTFState> state) {
light->outer_cone_angle = spot["outerConeAngle"];
ERR_CONTINUE_MSG(light->inner_cone_angle >= light->outer_cone_angle, "The inner angle must be smaller than the outer angle.");
} else if (type != "point" && type != "directional") {
- ERR_CONTINUE_MSG(ERR_PARSE_ERROR, "Light type is unknown.");
+ ERR_CONTINUE_MSG(true, "Light type is unknown.");
}
state->lights.push_back(light);
@@ -4503,8 +4637,8 @@ Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) {
const Dictionary &og = d["orthographic"];
// GLTF spec is in radians, Godot's camera is in degrees.
camera->set_fov_size(Math::rad2deg(real_t(og["ymag"])));
- camera->set_zfar(og["zfar"]);
- camera->set_znear(og["znear"]);
+ camera->set_depth_far(og["zfar"]);
+ camera->set_depth_near(og["znear"]);
} else {
camera->set_fov_size(10);
}
@@ -4514,8 +4648,8 @@ Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) {
const Dictionary &ppt = d["perspective"];
// GLTF spec is in radians, Godot's camera is in degrees.
camera->set_fov_size(Math::rad2deg(real_t(ppt["yfov"])));
- camera->set_zfar(ppt["zfar"]);
- camera->set_znear(ppt["znear"]);
+ camera->set_depth_far(ppt["zfar"]);
+ camera->set_depth_near(ppt["znear"]);
} else {
camera->set_fov_size(10);
}
@@ -4574,24 +4708,24 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
Array channels;
Array samplers;
- for (Map<int, GLTFAnimation::Track>::Element *track_i = gltf_animation->get_tracks().front(); track_i; track_i = track_i->next()) {
- GLTFAnimation::Track track = track_i->get();
- if (track.translation_track.times.size()) {
+ for (KeyValue<int, GLTFAnimation::Track> &track_i : gltf_animation->get_tracks()) {
+ GLTFAnimation::Track track = track_i.value;
+ if (track.position_track.times.size()) {
Dictionary t;
t["sampler"] = samplers.size();
Dictionary s;
- s["interpolation"] = interpolation_to_string(track.translation_track.interpolation);
- Vector<real_t> times = Variant(track.translation_track.times);
+ s["interpolation"] = interpolation_to_string(track.position_track.interpolation);
+ Vector<real_t> times = Variant(track.position_track.times);
s["input"] = _encode_accessor_as_floats(state, times, false);
- Vector<Vector3> values = Variant(track.translation_track.values);
+ Vector<Vector3> values = Variant(track.position_track.values);
s["output"] = _encode_accessor_as_vec3(state, values, false);
samplers.push_back(s);
Dictionary target;
target["path"] = "translation";
- target["node"] = track_i->key();
+ target["node"] = track_i.key;
t["target"] = target;
channels.push_back(t);
@@ -4611,7 +4745,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
Dictionary target;
target["path"] = "rotation";
- target["node"] = track_i->key();
+ target["node"] = track_i.key;
t["target"] = target;
channels.push_back(t);
@@ -4631,42 +4765,86 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
Dictionary target;
target["path"] = "scale";
- target["node"] = track_i->key();
+ target["node"] = track_i.key;
t["target"] = target;
channels.push_back(t);
}
if (track.weight_tracks.size()) {
+ double length = 0.0f;
+
+ for (int32_t track_idx = 0; track_idx < track.weight_tracks.size(); track_idx++) {
+ int32_t last_time_index = track.weight_tracks[track_idx].times.size() - 1;
+ length = MAX(length, track.weight_tracks[track_idx].times[last_time_index]);
+ }
+
Dictionary t;
t["sampler"] = samplers.size();
Dictionary s;
-
Vector<real_t> times;
- Vector<real_t> values;
+ const double increment = 1.0 / BAKE_FPS;
+ {
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ times.push_back(time);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= length) {
+ last = true;
+ time = length;
+ }
+ }
+ }
- for (int32_t times_i = 0; times_i < track.weight_tracks[0].times.size(); times_i++) {
- real_t time = track.weight_tracks[0].times[times_i];
- times.push_back(time);
+ for (int32_t track_idx = 0; track_idx < track.weight_tracks.size(); track_idx++) {
+ double time = 0.0;
+ bool last = false;
+ Vector<real_t> weight_track;
+ while (true) {
+ float weight = _interpolate_track<real_t>(track.weight_tracks[track_idx].times,
+ track.weight_tracks[track_idx].values,
+ time,
+ track.weight_tracks[track_idx].interpolation);
+ weight_track.push_back(weight);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= length) {
+ last = true;
+ time = length;
+ }
+ }
+ track.weight_tracks.write[track_idx].times = times;
+ track.weight_tracks.write[track_idx].values = weight_track;
}
- values.resize(times.size() * track.weight_tracks.size());
- // TODO Sort by order in blend shapes
+ Vector<real_t> all_track_times = times;
+ Vector<real_t> all_track_values;
+ int32_t values_size = track.weight_tracks[0].values.size();
+ int32_t weight_tracks_size = track.weight_tracks.size();
+ all_track_values.resize(weight_tracks_size * values_size);
for (int k = 0; k < track.weight_tracks.size(); k++) {
- Vector<float> wdata = track.weight_tracks[k].values;
+ Vector<real_t> wdata = track.weight_tracks[k].values;
for (int l = 0; l < wdata.size(); l++) {
- values.write[l * track.weight_tracks.size() + k] = wdata.write[l];
+ int32_t index = l * weight_tracks_size + k;
+ ERR_BREAK(index >= all_track_values.size());
+ all_track_values.write[index] = wdata.write[l];
}
}
s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation);
- s["input"] = _encode_accessor_as_floats(state, times, false);
- s["output"] = _encode_accessor_as_floats(state, values, false);
+ s["input"] = _encode_accessor_as_floats(state, all_track_times, false);
+ s["output"] = _encode_accessor_as_floats(state, all_track_values, false);
samplers.push_back(s);
Dictionary target;
target["path"] = "weights";
- target["node"] = track_i->key();
+ target["node"] = track_i.key;
t["target"] = target;
channels.push_back(t);
@@ -4679,6 +4857,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
}
}
+ if (!animations.size()) {
+ return OK;
+ }
state->json["animations"] = animations;
print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'.");
@@ -4769,10 +4950,10 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
const Vector<float> times = _decode_accessor_as_floats(state, input, false);
if (path == "translation") {
- const Vector<Vector3> translations = _decode_accessor_as_vec3(state, output, false);
- track->translation_track.interpolation = interp;
- track->translation_track.times = Variant(times); //convert via variant
- track->translation_track.values = Variant(translations); //convert via variant
+ const Vector<Vector3> positions = _decode_accessor_as_vec3(state, output, false);
+ track->position_track.interpolation = interp;
+ track->position_track.times = Variant(times); //convert via variant
+ track->position_track.values = Variant(positions); //convert via variant
} else if (path == "rotation") {
const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(state, output, false);
track->rotation_track.interpolation = interp;
@@ -4794,14 +4975,14 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
track->weight_tracks.resize(wc);
const int expected_value_count = times.size() * output_count * wc;
- ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
+ ERR_CONTINUE_MSG(weights.size() != expected_value_count, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
const int wlen = weights.size() / wc;
for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
- GLTFAnimation::Channel<float> cf;
+ GLTFAnimation::Channel<real_t> cf;
cf.interpolation = interp;
cf.times = Variant(times);
- Vector<float> wdata;
+ Vector<real_t> wdata;
wdata.resize(wlen);
for (int l = 0; l < wlen; l++) {
wdata.write[l] = weights[l * wc + k];
@@ -4859,77 +5040,82 @@ BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state,
return bone_attachment;
}
-GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshInstance3D *p_mesh_instance) {
+GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInstance3D *p_mesh_instance) {
ERR_FAIL_NULL_V(p_mesh_instance, -1);
if (p_mesh_instance->get_mesh().is_null()) {
return -1;
}
- Ref<EditorSceneImporterMesh> import_mesh;
- import_mesh.instantiate();
- Ref<Mesh> godot_mesh = p_mesh_instance->get_mesh();
- if (godot_mesh.is_null()) {
- return -1;
- }
+ Ref<ImporterMesh> current_mesh;
+ current_mesh.instantiate();
Vector<float> blend_weights;
- Vector<String> blend_names;
- int32_t blend_count = godot_mesh->get_blend_shape_count();
- blend_names.resize(blend_count);
- blend_weights.resize(blend_count);
- for (int32_t blend_i = 0; blend_i < godot_mesh->get_blend_shape_count(); blend_i++) {
- String blend_name = godot_mesh->get_blend_shape_name(blend_i);
- blend_names.write[blend_i] = blend_name;
- import_mesh->add_blend_shape(blend_name);
- }
- for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) {
- Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i);
- Array arrays = godot_mesh->surface_get_arrays(surface_i);
- Array blend_shape_arrays = godot_mesh->surface_get_blend_shape_arrays(surface_i);
- Ref<Material> mat = godot_mesh->surface_get_material(surface_i);
- Ref<ArrayMesh> godot_array_mesh = godot_mesh;
- String surface_name;
- if (godot_array_mesh.is_valid()) {
- surface_name = godot_array_mesh->surface_get_name(surface_i);
+ {
+ Ref<Mesh> import_mesh = p_mesh_instance->get_mesh();
+ Ref<ArrayMesh> import_array_mesh = p_mesh_instance->get_mesh();
+ if (import_mesh->get_blend_shape_count()) {
+ ArrayMesh::BlendShapeMode shape_mode = ArrayMesh::BLEND_SHAPE_MODE_NORMALIZED;
+ if (import_array_mesh.is_valid()) {
+ shape_mode = import_array_mesh->get_blend_shape_mode();
+ }
+ current_mesh->set_blend_shape_mode(shape_mode);
+ for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) {
+ current_mesh->add_blend_shape(import_mesh->get_blend_shape_name(morph_i));
+ }
+ }
+ for (int32_t surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) {
+ Array array = import_mesh->surface_get_arrays(surface_i);
+ Ref<Material> mat = import_mesh->surface_get_material(surface_i);
+ String mat_name;
+ if (mat.is_valid()) {
+ mat_name = mat->get_name();
+ }
+ current_mesh->add_surface(import_mesh->surface_get_primitive_type(surface_i),
+ array, import_mesh->surface_get_blend_shape_arrays(surface_i), import_mesh->surface_get_lods(surface_i), mat,
+ mat_name, import_mesh->surface_get_format(surface_i));
}
+ int32_t blend_count = import_mesh->get_blend_shape_count();
+ blend_weights.resize(blend_count);
+ for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) {
+ blend_weights.write[blend_i] = 0.0f;
+ }
+ }
+ Ref<GLTFMesh> gltf_mesh;
+ gltf_mesh.instantiate();
+ Array instance_materials;
+ for (int32_t surface_i = 0; surface_i < current_mesh->get_surface_count(); surface_i++) {
+ Ref<Material> mat = current_mesh->get_surface_material(surface_i);
if (p_mesh_instance->get_surface_override_material(surface_i).is_valid()) {
mat = p_mesh_instance->get_surface_override_material(surface_i);
}
if (p_mesh_instance->get_material_override().is_valid()) {
mat = p_mesh_instance->get_material_override();
}
- import_mesh->add_surface(primitive_type, arrays, blend_shape_arrays, Dictionary(), mat, surface_name);
+ instance_materials.append(mat);
}
- for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) {
- blend_weights.write[blend_i] = 0.0f;
- }
- Ref<GLTFMesh> gltf_mesh;
- gltf_mesh.instantiate();
- gltf_mesh->set_mesh(import_mesh);
+ gltf_mesh->set_instance_materials(instance_materials);
+ gltf_mesh->set_mesh(current_mesh);
gltf_mesh->set_blend_weights(blend_weights);
GLTFMeshIndex mesh_i = state->meshes.size();
state->meshes.push_back(gltf_mesh);
return mesh_i;
}
-EditorSceneImporterMeshNode3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
+ImporterMeshInstance3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
ERR_FAIL_INDEX_V(gltf_node->mesh, state->meshes.size(), nullptr);
- EditorSceneImporterMeshNode3D *mi = memnew(EditorSceneImporterMeshNode3D);
+ ImporterMeshInstance3D *mi = memnew(ImporterMeshInstance3D);
print_verbose("glTF: Creating mesh for: " + gltf_node->get_name());
Ref<GLTFMesh> mesh = state->meshes.write[gltf_node->mesh];
if (mesh.is_null()) {
return mi;
}
- Ref<EditorSceneImporterMesh> import_mesh = mesh->get_mesh();
+ Ref<ImporterMesh> import_mesh = mesh->get_mesh();
if (import_mesh.is_null()) {
return mi;
}
mi->set_mesh(import_mesh);
- for (int i = 0; i < mesh->get_blend_weights().size(); i++) {
- mi->set("blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i), mesh->get_blend_weights()[i]);
- }
return mi;
}
@@ -4950,7 +5136,7 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent,
intensity /= 100;
}
- if (l->type == "directional") {
+ if (l->light_type == "directional") {
DirectionalLight3D *light = memnew(DirectionalLight3D);
light->set_param(Light3D::PARAM_ENERGY, intensity);
light->set_color(l->color);
@@ -4960,15 +5146,15 @@ Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent,
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;
- if (l->type == "point") {
+ 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_RANGE, range);
light->set_color(l->color);
return light;
}
- if (l->type == "spot") {
+ if (l->light_type == "spot") {
SpotLight3D *light = memnew(SpotLight3D);
light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
light->set_param(SpotLight3D::PARAM_RANGE, range);
@@ -4995,9 +5181,9 @@ Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_paren
Ref<GLTFCamera> c = state->cameras[gltf_node->camera];
if (c->get_perspective()) {
- camera->set_perspective(c->get_fov_size(), c->get_znear(), c->get_zfar());
+ camera->set_perspective(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
} else {
- camera->set_orthogonal(c->get_fov_size(), c->get_znear(), c->get_zfar());
+ camera->set_orthogonal(c->get_fov_size(), c->get_depth_near(), c->get_depth_far());
}
return camera;
@@ -5011,14 +5197,10 @@ GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_
if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) {
c->set_perspective(true);
- c->set_fov_size(p_camera->get_fov());
- c->set_zfar(p_camera->get_far());
- c->set_znear(p_camera->get_near());
- } else {
- c->set_fov_size(p_camera->get_fov());
- c->set_zfar(p_camera->get_far());
- c->set_znear(p_camera->get_near());
}
+ c->set_fov_size(p_camera->get_fov());
+ c->set_depth_far(p_camera->get_far());
+ c->set_depth_near(p_camera->get_near());
GLTFCameraIndex camera_index = state->cameras.size();
state->cameras.push_back(c);
return camera_index;
@@ -5031,22 +5213,22 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig
l.instantiate();
l->color = p_light->get_color();
if (cast_to<DirectionalLight3D>(p_light)) {
- l->type = "directional";
+ l->light_type = "directional";
DirectionalLight3D *light = cast_to<DirectionalLight3D>(p_light);
l->intensity = light->get_param(DirectionalLight3D::PARAM_ENERGY);
l->range = FLT_MAX; // Range for directional lights is infinite in Godot.
} else if (cast_to<OmniLight3D>(p_light)) {
- l->type = "point";
+ 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;
+ l->intensity = l->range / (attenuation * 2048);
} else if (cast_to<SpotLight3D>(p_light)) {
- l->type = "spot";
+ 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;
+ l->intensity = l->range / (attenuation * 2048);
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).
@@ -5060,22 +5242,11 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_lig
return light_index;
}
-GLTFSkeletonIndex GLTFDocument::_convert_skeleton(Ref<GLTFState> state, Skeleton3D *p_skeleton) {
- print_verbose("glTF: Converting skeleton: " + p_skeleton->get_name());
- Ref<GLTFSkeleton> gltf_skeleton;
- gltf_skeleton.instantiate();
- gltf_skeleton->set_name(_gen_unique_name(state, p_skeleton->get_name()));
- gltf_skeleton->godot_skeleton = p_skeleton;
- GLTFSkeletonIndex skeleton_i = state->skeletons.size();
- state->skeletons.push_back(gltf_skeleton);
- return skeleton_i;
-}
-
void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node) {
Transform3D xform = p_spatial->get_transform();
p_node->scale = xform.basis.get_scale();
p_node->rotation = xform.basis.get_rotation_quaternion();
- p_node->translation = xform.origin;
+ p_node->position = xform.origin;
}
Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
@@ -5086,7 +5257,7 @@ Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent
return spatial;
}
-void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
+void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
bool retflag = true;
_check_visibility(p_current, retflag);
if (retflag) {
@@ -5100,37 +5271,41 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, No
_convert_spatial(state, spatial, gltf_node);
}
if (cast_to<MeshInstance3D>(p_current)) {
- Node3D *spatial = cast_to<Node3D>(p_current);
- _convert_mesh_to_gltf(p_current, state, spatial, gltf_node);
+ MeshInstance3D *mi = cast_to<MeshInstance3D>(p_current);
+ _convert_mesh_instance_to_gltf(mi, state, gltf_node);
} else if (cast_to<BoneAttachment3D>(p_current)) {
- _convert_bone_attachment_to_gltf(p_current, state, gltf_node, retflag);
- // TODO 2020-12-21 iFire Handle the case of objects under the bone attachment.
+ BoneAttachment3D *bone = cast_to<BoneAttachment3D>(p_current);
+ _convert_bone_attachment_to_gltf(bone, state, p_gltf_parent, p_gltf_root, gltf_node);
return;
} else if (cast_to<Skeleton3D>(p_current)) {
- _convert_skeleton_to_gltf(p_current, state, p_gltf_parent, p_gltf_root, gltf_node, p_root);
+ Skeleton3D *skel = cast_to<Skeleton3D>(p_current);
+ _convert_skeleton_to_gltf(skel, state, p_gltf_parent, p_gltf_root, gltf_node);
// We ignore the Godot Engine node that is the skeleton.
return;
} else if (cast_to<MultiMeshInstance3D>(p_current)) {
- _convert_mult_mesh_instance_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root);
+ MultiMeshInstance3D *multi = cast_to<MultiMeshInstance3D>(p_current);
+ _convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state);
#ifdef MODULE_CSG_ENABLED
} else if (cast_to<CSGShape3D>(p_current)) {
- if (p_current->get_parent() && cast_to<CSGShape3D>(p_current)->is_root_shape()) {
- _convert_csg_shape_to_gltf(p_current, p_gltf_parent, gltf_node, state);
+ CSGShape3D *shape = cast_to<CSGShape3D>(p_current);
+ if (shape->get_parent() && shape->is_root_shape()) {
+ _convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, state);
}
#endif // MODULE_CSG_ENABLED
#ifdef MODULE_GRIDMAP_ENABLED
} else if (cast_to<GridMap>(p_current)) {
- _convert_grid_map_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root);
+ GridMap *gridmap = Object::cast_to<GridMap>(p_current);
+ _convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, state);
#endif // MODULE_GRIDMAP_ENABLED
} else if (cast_to<Camera3D>(p_current)) {
Camera3D *camera = Object::cast_to<Camera3D>(p_current);
- _convert_camera_to_gltf(camera, state, camera, gltf_node);
+ _convert_camera_to_gltf(camera, state, gltf_node);
} else if (cast_to<Light3D>(p_current)) {
Light3D *light = Object::cast_to<Light3D>(p_current);
- _convert_light_to_gltf(light, state, light, gltf_node);
+ _convert_light_to_gltf(light, state, gltf_node);
} else if (cast_to<AnimationPlayer>(p_current)) {
AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current);
- _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current, p_root);
+ _convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
}
GLTFNodeIndex current_node_i = state->nodes.size();
GLTFNodeIndex gltf_root = p_gltf_root;
@@ -5142,13 +5317,13 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, No
}
_create_gltf_node(state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node);
for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) {
- _convert_scene_node(state, p_current->get_child(node_i), p_root, current_node_i, gltf_root);
+ _convert_scene_node(state, p_current->get_child(node_i), current_node_i, gltf_root);
}
}
#ifdef MODULE_CSG_ENABLED
-void GLTFDocument::_convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
- CSGShape3D *csg = Object::cast_to<CSGShape3D>(p_current);
+void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+ CSGShape3D *csg = p_current;
csg->call("_update_shape");
Array meshes = csg->get_meshes();
if (meshes.size() != 2) {
@@ -5160,13 +5335,8 @@ void GLTFDocument::_convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_g
}
Ref<GLTFMesh> gltf_mesh;
gltf_mesh.instantiate();
- Ref<EditorSceneImporterMesh> import_mesh;
- import_mesh.instantiate();
- Ref<ArrayMesh> array_mesh = csg->get_meshes()[1];
- for (int32_t surface_i = 0; surface_i < array_mesh->get_surface_count(); surface_i++) {
- import_mesh->add_surface(Mesh::PrimitiveType::PRIMITIVE_TRIANGLES, array_mesh->surface_get_arrays(surface_i), Array(), Dictionary(), mat, array_mesh->surface_get_name(surface_i));
- }
- gltf_mesh->set_mesh(import_mesh);
+ Ref<ImporterMesh> array_mesh = csg->get_meshes()[1];
+ gltf_mesh->set_mesh(array_mesh);
GLTFMeshIndex mesh_i = state->meshes.size();
state->meshes.push_back(gltf_mesh);
gltf_node->mesh = mesh_i;
@@ -5179,16 +5349,15 @@ void GLTFDocument::_create_gltf_node(Ref<GLTFState> state, Node *p_scene_parent,
GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref<GLTFNode> gltf_node) {
state->scene_nodes.insert(current_node_i, p_scene_parent);
state->nodes.push_back(gltf_node);
- if (current_node_i == p_parent_node_index) {
- return;
- }
+ ERR_FAIL_COND(current_node_i == p_parent_node_index);
+ state->nodes.write[current_node_i]->parent = p_parent_node_index;
if (p_parent_node_index == -1) {
return;
}
state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
}
-void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, const GLTFNodeIndex &p_gltf_current, const GLTFNodeIndex &p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent, Node *p_root) {
+void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
ERR_FAIL_COND(!animation_player);
state->animation_players.push_back(animation_player);
print_verbose(String("glTF: Converting animation player: ") + animation_player->get_name());
@@ -5207,7 +5376,7 @@ void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) {
retflag = false;
}
-void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
ERR_FAIL_COND(!camera);
GLTFCameraIndex camera_index = _convert_camera(state, camera);
if (camera_index != -1) {
@@ -5215,7 +5384,7 @@ void GLTFDocument::_convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> stat
}
}
-void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
ERR_FAIL_COND(!light);
GLTFLightIndex light_index = _convert_light(state, light);
if (light_index != -1) {
@@ -5224,131 +5393,196 @@ void GLTFDocument::_convert_light_to_gltf(Light3D *light, Ref<GLTFState> state,
}
#ifdef MODULE_GRIDMAP_ENABLED
-void GLTFDocument::_convert_grid_map_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) {
- GridMap *grid_map = Object::cast_to<GridMap>(p_scene_parent);
- ERR_FAIL_COND(!grid_map);
- Array cells = grid_map->get_used_cells();
+void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+ Array cells = p_grid_map->get_used_cells();
for (int32_t k = 0; k < cells.size(); k++) {
GLTFNode *new_gltf_node = memnew(GLTFNode);
gltf_node->children.push_back(state->nodes.size());
state->nodes.push_back(new_gltf_node);
Vector3 cell_location = cells[k];
- int32_t cell = grid_map->get_cell_item(
+ int32_t cell = p_grid_map->get_cell_item(
Vector3(cell_location.x, cell_location.y, cell_location.z));
- EditorSceneImporterMeshNode3D *import_mesh_node = memnew(EditorSceneImporterMeshNode3D);
- import_mesh_node->set_mesh(grid_map->get_mesh_library()->get_item_mesh(cell));
+ ImporterMeshInstance3D *import_mesh_node = memnew(ImporterMeshInstance3D);
+ import_mesh_node->set_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell));
Transform3D cell_xform;
cell_xform.basis.set_orthogonal_index(
- grid_map->get_cell_item_orientation(
+ p_grid_map->get_cell_item_orientation(
Vector3(cell_location.x, cell_location.y, cell_location.z)));
- cell_xform.basis.scale(Vector3(grid_map->get_cell_scale(),
- grid_map->get_cell_scale(),
- grid_map->get_cell_scale()));
- cell_xform.set_origin(grid_map->map_to_world(
+ cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(),
+ p_grid_map->get_cell_scale(),
+ p_grid_map->get_cell_scale()));
+ cell_xform.set_origin(p_grid_map->map_to_world(
Vector3(cell_location.x, cell_location.y, cell_location.z)));
Ref<GLTFMesh> gltf_mesh;
gltf_mesh.instantiate();
gltf_mesh = import_mesh_node;
new_gltf_node->mesh = state->meshes.size();
state->meshes.push_back(gltf_mesh);
- new_gltf_node->xform = cell_xform * grid_map->get_transform();
- new_gltf_node->set_name(_gen_unique_name(state, grid_map->get_mesh_library()->get_item_name(cell)));
+ new_gltf_node->xform = cell_xform * p_grid_map->get_transform();
+ new_gltf_node->set_name(_gen_unique_name(state, p_grid_map->get_mesh_library()->get_item_name(cell)));
}
}
#endif // MODULE_GRIDMAP_ENABLED
-void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) {
- MultiMeshInstance3D *multi_mesh_instance = Object::cast_to<MultiMeshInstance3D>(p_scene_parent);
- ERR_FAIL_COND(!multi_mesh_instance);
- Ref<MultiMesh> multi_mesh = multi_mesh_instance->get_multimesh();
- if (multi_mesh.is_valid()) {
- for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
- instance_i++) {
- GLTFNode *new_gltf_node = memnew(GLTFNode);
- Transform3D transform;
- if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) {
- Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i);
- transform.origin =
- Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y);
- real_t rotation = xform_2d.get_rotation();
- Quaternion quaternion(Vector3(0, 1, 0), rotation);
- Size2 scale = xform_2d.get_scale();
- transform.basis.set_quaternion_scale(quaternion,
- Vector3(scale.x, 0, scale.y));
- transform =
- multi_mesh_instance->get_transform() * transform;
- } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) {
- transform = multi_mesh_instance->get_transform() *
- multi_mesh->get_instance_transform(instance_i);
- }
- Ref<ArrayMesh> mm = multi_mesh->get_mesh();
- if (mm.is_valid()) {
- Ref<EditorSceneImporterMesh> mesh;
- mesh.instantiate();
- for (int32_t surface_i = 0; surface_i < mm->get_surface_count(); surface_i++) {
- Array surface = mm->surface_get_arrays(surface_i);
- mesh->add_surface(mm->surface_get_primitive_type(surface_i), surface, Array(), Dictionary(),
- mm->surface_get_material(surface_i), mm->get_name());
- }
- Ref<GLTFMesh> gltf_mesh;
- gltf_mesh.instantiate();
- gltf_mesh->set_name(multi_mesh->get_name());
- gltf_mesh->set_mesh(mesh);
- new_gltf_node->mesh = state->meshes.size();
- state->meshes.push_back(gltf_mesh);
- }
- new_gltf_node->xform = transform;
- new_gltf_node->set_name(_gen_unique_name(state, multi_mesh_instance->get_name()));
- gltf_node->children.push_back(state->nodes.size());
- state->nodes.push_back(new_gltf_node);
+void GLTFDocument::_convert_multi_mesh_instance_to_gltf(
+ MultiMeshInstance3D *p_multi_mesh_instance,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_node_index,
+ Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+ ERR_FAIL_COND(!p_multi_mesh_instance);
+ Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh();
+ if (multi_mesh.is_null()) {
+ return;
+ }
+ Ref<GLTFMesh> gltf_mesh;
+ gltf_mesh.instantiate();
+ Ref<Mesh> mesh = multi_mesh->get_mesh();
+ if (mesh.is_null()) {
+ return;
+ }
+ gltf_mesh->set_name(multi_mesh->get_name());
+ Ref<ImporterMesh> importer_mesh;
+ importer_mesh.instantiate();
+ Ref<ArrayMesh> array_mesh = multi_mesh->get_mesh();
+ if (array_mesh.is_valid()) {
+ importer_mesh->set_blend_shape_mode(array_mesh->get_blend_shape_mode());
+ for (int32_t blend_i = 0; blend_i < array_mesh->get_blend_shape_count(); blend_i++) {
+ importer_mesh->add_blend_shape(array_mesh->get_blend_shape_name(blend_i));
}
}
-}
-
-void GLTFDocument::_convert_skeleton_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Node *p_root_node) {
- Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_scene_parent);
- if (skeleton) {
- // Remove placeholder skeleton3d node by not creating the gltf node
- // Skins are per mesh
- for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) {
- _convert_scene_node(state, skeleton->get_child(node_i), p_root_node, p_parent_node_index, p_root_node_index);
+ for (int32_t surface_i = 0; surface_i < mesh->get_surface_count(); surface_i++) {
+ Ref<Material> mat = mesh->surface_get_material(surface_i);
+ String material_name;
+ if (mat.is_valid()) {
+ material_name = mat->get_name();
+ }
+ Array blend_arrays;
+ if (array_mesh.is_valid()) {
+ blend_arrays = array_mesh->surface_get_blend_shape_arrays(surface_i);
}
+ importer_mesh->add_surface(mesh->surface_get_primitive_type(surface_i), mesh->surface_get_arrays(surface_i),
+ blend_arrays, mesh->surface_get_lods(surface_i), mat, material_name, mesh->surface_get_format(surface_i));
+ }
+ gltf_mesh->set_mesh(importer_mesh);
+ GLTFMeshIndex mesh_index = state->meshes.size();
+ state->meshes.push_back(gltf_mesh);
+ for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
+ instance_i++) {
+ Transform3D transform;
+ if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_2D) {
+ Transform2D xform_2d = multi_mesh->get_instance_transform_2d(instance_i);
+ transform.origin =
+ Vector3(xform_2d.get_origin().x, 0, xform_2d.get_origin().y);
+ real_t rotation = xform_2d.get_rotation();
+ Quaternion quaternion(Vector3(0, 1, 0), rotation);
+ Size2 scale = xform_2d.get_scale();
+ transform.basis.set_quaternion_scale(quaternion,
+ Vector3(scale.x, 0, scale.y));
+ transform = p_multi_mesh_instance->get_transform() * transform;
+ } else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) {
+ transform = p_multi_mesh_instance->get_transform() *
+ multi_mesh->get_instance_transform(instance_i);
+ }
+ Ref<GLTFNode> new_gltf_node;
+ new_gltf_node.instantiate();
+ new_gltf_node->mesh = mesh_index;
+ new_gltf_node->xform = transform;
+ new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name()));
+ gltf_node->children.push_back(state->nodes.size());
+ state->nodes.push_back(new_gltf_node);
}
}
-void GLTFDocument::_convert_bone_attachment_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node, bool &retflag) {
- retflag = true;
- BoneAttachment3D *bone_attachment = Object::cast_to<BoneAttachment3D>(p_scene_parent);
- if (bone_attachment) {
- Node *node = bone_attachment->get_parent();
- while (node) {
- Skeleton3D *bone_attachment_skeleton = Object::cast_to<Skeleton3D>(node);
- if (bone_attachment_skeleton) {
- for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
- if (state->skeletons[skeleton_i]->godot_skeleton != bone_attachment_skeleton) {
- continue;
- }
- state->skeletons.write[skeleton_i]->bone_attachments.push_back(bone_attachment);
- break;
- }
- break;
+void GLTFDocument::_convert_skeleton_to_gltf(Skeleton3D *p_skeleton3d, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+ Skeleton3D *skeleton = p_skeleton3d;
+ Ref<GLTFSkeleton> gltf_skeleton;
+ gltf_skeleton.instantiate();
+ // GLTFSkeleton is only used to hold internal state data. It will not be written to the document.
+ //
+ gltf_skeleton->godot_skeleton = skeleton;
+ GLTFSkeletonIndex skeleton_i = state->skeletons.size();
+ state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skeleton_i;
+ state->skeletons.push_back(gltf_skeleton);
+
+ BoneId bone_count = skeleton->get_bone_count();
+ for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) {
+ Ref<GLTFNode> joint_node;
+ joint_node.instantiate();
+ // Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node
+ // names to be unique regardless of whether or not they are used as joints.
+ joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i)));
+ Transform3D xform = skeleton->get_bone_pose(bone_i);
+ joint_node->scale = xform.basis.get_scale();
+ joint_node->rotation = xform.basis.get_rotation_quaternion();
+ joint_node->position = xform.origin;
+ joint_node->joint = true;
+ GLTFNodeIndex current_node_i = state->nodes.size();
+ state->scene_nodes.insert(current_node_i, skeleton);
+ state->nodes.push_back(joint_node);
+
+ gltf_skeleton->joints.push_back(current_node_i);
+ if (skeleton->get_bone_parent(bone_i) == -1) {
+ gltf_skeleton->roots.push_back(current_node_i);
+ }
+ gltf_skeleton->godot_bone_node.insert(bone_i, current_node_i);
+ }
+ for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) {
+ GLTFNodeIndex current_node_i = gltf_skeleton->godot_bone_node[bone_i];
+ BoneId parent_bone_id = skeleton->get_bone_parent(bone_i);
+ if (parent_bone_id == -1) {
+ if (p_parent_node_index != -1) {
+ state->nodes.write[current_node_i]->parent = p_parent_node_index;
+ state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
}
- node = node->get_parent();
+ } else {
+ GLTFNodeIndex parent_node_i = gltf_skeleton->godot_bone_node[parent_bone_id];
+ state->nodes.write[current_node_i]->parent = parent_node_i;
+ state->nodes.write[parent_node_i]->children.push_back(current_node_i);
}
- gltf_node.unref();
- return;
}
- retflag = false;
+ // Remove placeholder skeleton3d node by not creating the gltf node
+ // Skins are per mesh
+ for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) {
+ _convert_scene_node(state, skeleton->get_child(node_i), p_parent_node_index, p_root_node_index);
+ }
}
-void GLTFDocument::_convert_mesh_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node) {
- MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_scene_parent);
- if (mi) {
- GLTFMeshIndex gltf_mesh_index = _convert_mesh_instance(state, mi);
- if (gltf_mesh_index != -1) {
- gltf_node->mesh = gltf_mesh_index;
+void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+ Skeleton3D *skeleton;
+ // Note that relative transforms to external skeletons and pose overrides are not supported.
+ if (p_bone_attachment->get_use_external_skeleton()) {
+ skeleton = cast_to<Skeleton3D>(p_bone_attachment->get_node_or_null(p_bone_attachment->get_external_skeleton()));
+ } else {
+ skeleton = cast_to<Skeleton3D>(p_bone_attachment->get_parent());
+ }
+ GLTFSkeletonIndex skel_gltf_i = -1;
+ if (skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) {
+ skel_gltf_i = state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()];
+ }
+ int bone_idx = -1;
+ if (skeleton != nullptr) {
+ bone_idx = p_bone_attachment->get_bone_idx();
+ if (bone_idx == -1) {
+ bone_idx = skeleton->find_bone(p_bone_attachment->get_bone_name());
}
}
+ GLTFNodeIndex par_node_index = p_parent_node_index;
+ if (skeleton != nullptr && bone_idx != -1 && skel_gltf_i != -1) {
+ Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_gltf_i];
+ gltf_skeleton->bone_attachments.push_back(p_bone_attachment);
+ par_node_index = gltf_skeleton->joints[bone_idx];
+ }
+
+ for (int node_i = 0; node_i < p_bone_attachment->get_child_count(); node_i++) {
+ _convert_scene_node(state, p_bone_attachment->get_child(node_i), par_node_index, p_root_node_index);
+ }
+}
+
+void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance3D *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
+ GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf(state, p_scene_parent);
+ if (gltf_mesh_index != -1) {
+ gltf_node->mesh = gltf_mesh_index;
+ }
}
void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
@@ -5371,7 +5605,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
// Bone Attachment - Parent Case
BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
- scene_parent->add_child(bone_attachment);
+ scene_parent->add_child(bone_attachment, true);
bone_attachment->set_owner(scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
@@ -5394,7 +5628,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
current_node = _generate_spatial(state, scene_parent, 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);
}
@@ -5424,7 +5658,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
// Bone Attachment - Direct Parented Skeleton Case
BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
- scene_parent->add_child(bone_attachment);
+ scene_parent->add_child(bone_attachment, true);
bone_attachment->set_owner(scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
@@ -5438,7 +5672,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
// Add it to the scene if it has not already been added
if (skeleton->get_parent() == nullptr) {
- scene_parent->add_child(skeleton);
+ scene_parent->add_child(skeleton, true);
skeleton->set_owner(scene_root);
}
}
@@ -5452,7 +5686,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
// Bone Attachment - Same Node Case
BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index);
- scene_parent->add_child(bone_attachment);
+ scene_parent->add_child(bone_attachment, true);
bone_attachment->set_owner(scene_root);
// There is no gltf_node that represent this, so just directly create a unique name
@@ -5472,7 +5706,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
current_node = _generate_light(state, scene_parent, 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);
}
@@ -5488,7 +5722,7 @@ void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scen
}
template <class T>
-struct EditorSceneImporterGLTFInterpolate {
+struct EditorSceneFormatImporterGLTFInterpolate {
T lerp(const T &a, const T &b, float c) const {
return a + (b - a) * c;
}
@@ -5514,7 +5748,7 @@ struct EditorSceneImporterGLTFInterpolate {
// thank you for existing, partial specialization
template <>
-struct EditorSceneImporterGLTFInterpolate<Quaternion> {
+struct EditorSceneFormatImporterGLTFInterpolate<Quaternion> {
Quaternion lerp(const Quaternion &a, const Quaternion &b, const float c) const {
ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quaternion(), "The quaternion \"a\" must be normalized.");
ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quaternion(), "The quaternion \"b\" must be normalized.");
@@ -5538,9 +5772,9 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> {
};
template <class T>
-T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
+T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
ERR_FAIL_COND_V(!p_values.size(), T());
- if (p_times.size() != p_values.size()) {
+ if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
return p_values[0];
}
@@ -5553,7 +5787,7 @@ T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T>
idx++;
}
- EditorSceneImporterGLTFInterpolate<T> interp;
+ EditorSceneFormatImporterGLTFInterpolate<T> interp;
switch (p_interp) {
case GLTFAnimation::INTERP_LINEAR: {
@@ -5622,21 +5856,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
animation->set_name(name);
if (anim->get_loop()) {
- animation->set_loop(true);
+ animation->set_loop_mode(Animation::LOOP_LINEAR);
}
float length = 0.0;
- for (Map<int, GLTFAnimation::Track>::Element *track_i = anim->get_tracks().front(); track_i; track_i = track_i->next()) {
- const GLTFAnimation::Track &track = track_i->get();
+ for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) {
+ const GLTFAnimation::Track &track = track_i.value;
//need to find the path: for skeletons, weight tracks will affect the mesh
NodePath node_path;
//for skeletons, transform tracks always affect bones
NodePath transform_node_path;
- GLTFNodeIndex node_index = track_i->key();
+ GLTFNodeIndex node_index = track_i.key;
- const Ref<GLTFNode> gltf_node = state->nodes[track_i->key()];
+ const Ref<GLTFNode> gltf_node = state->nodes[track_i.key];
Node *root = ap->get_parent();
ERR_FAIL_COND(root == nullptr);
@@ -5658,8 +5892,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (int i = 0; i < track.rotation_track.times.size(); i++) {
length = MAX(length, track.rotation_track.times[i]);
}
- for (int i = 0; i < track.translation_track.times.size(); i++) {
- length = MAX(length, track.translation_track.times[i]);
+ for (int i = 0; i < track.position_track.times.size(); i++) {
+ length = MAX(length, track.position_track.times[i]);
}
for (int i = 0; i < track.scale_track.times.size(); i++) {
length = MAX(length, track.scale_track.times[i]);
@@ -5673,11 +5907,69 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
// Animated TRS properties will not affect a skinned mesh.
const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0;
- if ((track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
+ if ((track.rotation_track.values.size() || track.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
//make transform track
- int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM3D);
- animation->track_set_path(track_idx, transform_node_path);
+ int base_idx = animation->get_track_count();
+ int position_idx = -1;
+ int rotation_idx = -1;
+ int scale_idx = -1;
+
+ if (track.position_track.values.size()) {
+ Vector3 base_pos = state->nodes[track_i.key]->position;
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.position_track.times.size(); i++) {
+ Vector3 value = track.position_track.values[track.position_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ if (!value.is_equal_approx(base_pos)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ position_idx = base_idx;
+ animation->add_track(Animation::TYPE_POSITION_3D);
+ animation->track_set_path(position_idx, transform_node_path);
+ animation->track_set_imported(position_idx, true); //helps merging later
+
+ base_idx++;
+ }
+ }
+ if (track.rotation_track.values.size()) {
+ Quaternion base_rot = state->nodes[track_i.key]->rotation.normalized();
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.rotation_track.times.size(); i++) {
+ Quaternion value = track.rotation_track.values[track.rotation_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i].normalized();
+ if (!value.is_equal_approx(base_rot)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ rotation_idx = base_idx;
+ animation->add_track(Animation::TYPE_ROTATION_3D);
+ animation->track_set_path(rotation_idx, transform_node_path);
+ animation->track_set_imported(rotation_idx, true); //helps merging later
+ base_idx++;
+ }
+ }
+ if (track.scale_track.values.size()) {
+ Vector3 base_scale = state->nodes[track_i.key]->scale;
+ bool not_default = false; //discard the track if all it contains is default values
+ for (int i = 0; i < track.scale_track.times.size(); i++) {
+ Vector3 value = track.scale_track.values[track.scale_track.interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE ? (1 + i * 3) : i];
+ if (!value.is_equal_approx(base_scale)) {
+ not_default = true;
+ break;
+ }
+ }
+ if (not_default) {
+ scale_idx = base_idx;
+ animation->add_track(Animation::TYPE_SCALE_3D);
+ animation->track_set_path(scale_idx, transform_node_path);
+ animation->track_set_imported(scale_idx, true); //helps merging later
+ base_idx++;
+ }
+ }
+
//first determine animation length
const double increment = 1.0 / bake_fps;
@@ -5687,16 +5979,16 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion base_rot;
Vector3 base_scale = Vector3(1, 1, 1);
- if (!track.rotation_track.values.size()) {
- base_rot = state->nodes[track_i->key()]->rotation.normalized();
+ if (rotation_idx == -1) {
+ base_rot = state->nodes[track_i.key]->rotation.normalized();
}
- if (!track.translation_track.values.size()) {
- base_pos = state->nodes[track_i->key()]->translation;
+ if (position_idx == -1) {
+ base_pos = state->nodes[track_i.key]->position;
}
- if (!track.scale_track.values.size()) {
- base_scale = state->nodes[track_i->key()]->scale;
+ if (scale_idx == -1) {
+ base_scale = state->nodes[track_i.key]->scale;
}
bool last = false;
@@ -5705,35 +5997,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Quaternion rot = base_rot;
Vector3 scale = base_scale;
- if (track.translation_track.times.size()) {
- pos = _interpolate_track<Vector3>(track.translation_track.times, track.translation_track.values, time, track.translation_track.interpolation);
+ if (position_idx >= 0) {
+ pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
+ animation->position_track_insert_key(position_idx, time, pos);
}
- if (track.rotation_track.times.size()) {
+ if (rotation_idx >= 0) {
rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
+ animation->rotation_track_insert_key(rotation_idx, time, rot);
}
- if (track.scale_track.times.size()) {
+ if (scale_idx >= 0) {
scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
+ animation->scale_track_insert_key(scale_idx, time, scale);
}
- if (gltf_node->skeleton >= 0) {
- Transform3D xform;
- xform.basis.set_quaternion_scale(rot, scale);
- xform.origin = pos;
-
- const Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
- const int bone_idx = skeleton->find_bone(gltf_node->get_name());
- xform = skeleton->get_bone_rest(bone_idx).affine_inverse() * xform;
-
- rot = xform.basis.get_rotation_quaternion();
- rot.normalize();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
-
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
-
if (last) {
break;
}
@@ -5751,12 +6029,11 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
ERR_CONTINUE(mesh.is_null());
ERR_CONTINUE(mesh->get_mesh().is_null());
ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null());
- const String prop = "blend_shapes/" + mesh->get_mesh()->get_blend_shape_name(i);
- const String blend_path = String(node_path) + ":" + prop;
+ const String blend_path = String(node_path) + ":" + String(mesh->get_mesh()->get_blend_shape_name(i));
const int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_VALUE);
+ animation->add_track(Animation::TYPE_BLEND_SHAPE);
animation->track_set_path(track_idx, blend_path);
// Only LINEAR and STEP (NEAREST) can be supported out of the box by Godot's Animation,
@@ -5767,7 +6044,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
const float t = track.weight_tracks[i].times[j];
const float attribs = track.weight_tracks[i].values[j];
- animation->track_insert_key(track_idx, t, attribs);
+ animation->blend_shape_track_insert_key(track_idx, t, attribs);
}
} else {
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
@@ -5775,7 +6052,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
double time = 0.0;
bool last = false;
while (true) {
- _interpolate_track<float>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
+ real_t blend = _interpolate_track<real_t>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
+ animation->blend_shape_track_insert_key(track_idx, time, blend);
if (last) {
break;
}
@@ -5801,10 +6079,6 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
if (node->mesh < 0) {
continue;
}
- Array json_skins;
- if (state->json.has("skins")) {
- json_skins = state->json["skins"];
- }
Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
if (!mi_element) {
continue;
@@ -5814,9 +6088,8 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
Transform3D mi_xform = mi->get_transform();
node->scale = mi_xform.basis.get_scale();
node->rotation = mi_xform.basis.get_rotation_quaternion();
- node->translation = mi_xform.origin;
+ node->position = mi_xform.origin;
- Dictionary json_skin;
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(mi->get_node(mi->get_skeleton_path()));
if (!skeleton) {
continue;
@@ -5825,121 +6098,78 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
continue;
}
Ref<Skin> skin = mi->get_skin();
- if (skin.is_null()) {
- skin = skeleton->register_skin(nullptr)->get_skin();
- }
Ref<GLTFSkin> gltf_skin;
gltf_skin.instantiate();
Array json_joints;
- GLTFSkeletonIndex skeleton_gltf_i = -1;
NodePath skeleton_path = mi->get_skeleton_path();
- bool is_unique = true;
- for (int32_t skin_i = 0; skin_i < state->skins.size(); skin_i++) {
- Ref<GLTFSkin> prev_gltf_skin = state->skins.write[skin_i];
- if (gltf_skin.is_null()) {
- continue;
- }
- GLTFSkeletonIndex prev_skeleton = prev_gltf_skin->get_skeleton();
- if (prev_skeleton == -1 || prev_skeleton >= state->skeletons.size()) {
- continue;
- }
- if (prev_gltf_skin->get_godot_skin() == skin && state->skeletons[prev_skeleton]->godot_skeleton == skeleton) {
- node->skin = skin_i;
- node->skeleton = prev_skeleton;
- is_unique = false;
- break;
- }
- }
- if (!is_unique) {
- continue;
- }
- GLTFSkeletonIndex skeleton_i = _convert_skeleton(state, skeleton);
- skeleton_gltf_i = skeleton_i;
- ERR_CONTINUE(skeleton_gltf_i == -1);
- gltf_skin->skeleton = skeleton_gltf_i;
- Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skeleton_gltf_i];
- for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) {
- String godot_bone_name = skin->get_bind_name(bind_i);
- if (godot_bone_name.is_empty()) {
- int32_t bone = skin->get_bind_bone(bind_i);
- godot_bone_name = skeleton->get_bone_name(bone);
- }
- if (skeleton->find_bone(godot_bone_name) == -1) {
- godot_bone_name = skeleton->get_bone_name(0);
- }
- BoneId bone_index = skeleton->find_bone(godot_bone_name);
- ERR_CONTINUE(bone_index == -1);
- Ref<GLTFNode> joint_node;
- joint_node.instantiate();
- String gltf_bone_name = _gen_unique_bone_name(state, skeleton_gltf_i, godot_bone_name);
- joint_node->set_name(gltf_bone_name);
-
- Transform3D bone_rest_xform = skeleton->get_bone_rest(bone_index);
- joint_node->scale = bone_rest_xform.basis.get_scale();
- joint_node->rotation = bone_rest_xform.basis.get_rotation_quaternion();
- joint_node->translation = bone_rest_xform.origin;
- joint_node->joint = true;
-
- int32_t joint_node_i = state->nodes.size();
- state->nodes.push_back(joint_node);
- gltf_skeleton->godot_bone_node.insert(bone_index, joint_node_i);
- int32_t joint_index = gltf_skin->joints.size();
- gltf_skin->joint_i_to_bone_i.insert(joint_index, bone_index);
- gltf_skin->joints.push_back(joint_node_i);
- gltf_skin->joints_original.push_back(joint_node_i);
- gltf_skin->inverse_binds.push_back(skin->get_bind_pose(bind_i));
- json_joints.push_back(joint_node_i);
- for (Map<GLTFNodeIndex, Node *>::Element *skin_scene_node_i = state->scene_nodes.front(); skin_scene_node_i; skin_scene_node_i = skin_scene_node_i->next()) {
- if (skin_scene_node_i->get() == skeleton) {
- gltf_skin->skin_root = skin_scene_node_i->key();
- json_skin["skeleton"] = skin_scene_node_i->key();
- }
- }
- gltf_skin->godot_skin = skin;
- gltf_skin->set_name(_gen_unique_name(state, skin->get_name()));
- }
- for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) {
- String bone_name = skeleton->get_bone_name(bind_i);
- String godot_bone_name = skin->get_bind_name(bind_i);
- int32_t bone = -1;
- if (skin->get_bind_bone(bind_i) != -1) {
- bone = skin->get_bind_bone(bind_i);
- godot_bone_name = skeleton->get_bone_name(bone);
- }
- bone = skeleton->find_bone(godot_bone_name);
- if (bone == -1) {
- continue;
- }
- BoneId bone_parent = skeleton->get_bone_parent(bone);
- GLTFNodeIndex joint_node_i = gltf_skeleton->godot_bone_node[bone];
- ERR_CONTINUE(joint_node_i >= state->nodes.size());
- if (bone_parent != -1) {
- GLTFNodeIndex parent_joint_gltf_node = gltf_skin->joints[bone_parent];
- Ref<GLTFNode> parent_joint_node = state->nodes.write[parent_joint_gltf_node];
- parent_joint_node->children.push_back(joint_node_i);
+ Node *skel_node = mi->get_node_or_null(skeleton_path);
+ Skeleton3D *godot_skeleton = nullptr;
+ if (skel_node != nullptr) {
+ godot_skeleton = cast_to<Skeleton3D>(skel_node);
+ }
+ if (godot_skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) {
+ // This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible.
+ const GLTFSkeletonIndex skeleton_gltf_i = state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()];
+ Ref<GLTFSkeleton> gltf_skeleton = state->skeletons[skeleton_gltf_i];
+ int bone_cnt = skeleton->get_bone_count();
+ ERR_FAIL_COND(bone_cnt != gltf_skeleton->joints.size());
+
+ ObjectID gltf_skin_key;
+ if (skin.is_valid()) {
+ gltf_skin_key = skin->get_instance_id();
+ }
+ ObjectID gltf_skel_key = godot_skeleton->get_instance_id();
+ GLTFSkinIndex skin_gltf_i = -1;
+ GLTFNodeIndex root_gltf_i = -1;
+ if (!gltf_skeleton->roots.is_empty()) {
+ root_gltf_i = gltf_skeleton->roots[0];
+ }
+ if (state->skin_and_skeleton3d_to_gltf_skin.has(gltf_skin_key) && state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key].has(gltf_skel_key)) {
+ skin_gltf_i = state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key];
} else {
- Node *node_parent = skeleton->get_parent();
- ERR_CONTINUE(!node_parent);
- for (Map<GLTFNodeIndex, Node *>::Element *E = state->scene_nodes.front(); E; E = E->next()) {
- if (E->get() == node_parent) {
- GLTFNodeIndex gltf_node_i = E->key();
- Ref<GLTFNode> gltf_node = state->nodes.write[gltf_node_i];
- gltf_node->children.push_back(joint_node_i);
- break;
+ if (skin.is_null()) {
+ // Note that gltf_skin_key should remain null, so these can share a reference.
+ skin = skeleton->create_skin_from_rest_transforms();
+ }
+ gltf_skin.instantiate();
+ gltf_skin->godot_skin = skin;
+ gltf_skin->set_name(skin->get_name());
+ gltf_skin->skeleton = skeleton_gltf_i;
+ gltf_skin->skin_root = root_gltf_i;
+ //gltf_state->godot_to_gltf_node[skel_node]
+ HashMap<StringName, int> bone_name_to_idx;
+ for (int bone_i = 0; bone_i < bone_cnt; bone_i++) {
+ bone_name_to_idx[skeleton->get_bone_name(bone_i)] = bone_i;
+ }
+ for (int bind_i = 0, cnt = skin->get_bind_count(); bind_i < cnt; bind_i++) {
+ int bone_i = skin->get_bind_bone(bind_i);
+ Transform3D bind_pose = skin->get_bind_pose(bind_i);
+ StringName bind_name = skin->get_bind_name(bind_i);
+ if (bind_name != StringName()) {
+ bone_i = bone_name_to_idx[bind_name];
+ }
+ ERR_CONTINUE(bone_i < 0 || bone_i >= bone_cnt);
+ if (bind_name == StringName()) {
+ bind_name = skeleton->get_bone_name(bone_i);
+ }
+ GLTFNodeIndex skeleton_bone_i = gltf_skeleton->joints[bone_i];
+ gltf_skin->joints_original.push_back(skeleton_bone_i);
+ gltf_skin->joints.push_back(skeleton_bone_i);
+ gltf_skin->inverse_binds.push_back(bind_pose);
+ if (skeleton->get_bone_parent(bone_i) == -1) {
+ gltf_skin->roots.push_back(skeleton_bone_i);
}
+ gltf_skin->joint_i_to_bone_i[bind_i] = bone_i;
+ gltf_skin->joint_i_to_name[bind_i] = bind_name;
}
+ skin_gltf_i = state->skins.size();
+ state->skins.push_back(gltf_skin);
+ state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key] = skin_gltf_i;
}
+ node->skin = skin_gltf_i;
+ node->skeleton = skeleton_gltf_i;
}
- _expand_skin(state, gltf_skin);
- node->skin = state->skins.size();
- state->skins.push_back(gltf_skin);
-
- json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false);
- json_skin["joints"] = json_joints;
- json_skin["name"] = gltf_skin->get_name();
- json_skins.push_back(json_skin);
- state->json["skins"] = json_skins;
}
}
@@ -5984,8 +6214,8 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
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));
- EditorSceneImporterMeshNode3D *mi = Object::cast_to<EditorSceneImporterMeshNode3D>(mi_element->get());
- ERR_CONTINUE_MSG(mi == nullptr, vformat("Unable to cast node %d of type %s to EditorSceneImporterMeshNode3D", node_i, mi_element->get()->get_class_name()));
+ 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()));
const GLTFSkeletonIndex skel_i = state->skins.write[node->skin]->skeleton;
Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i];
@@ -5993,7 +6223,7 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
ERR_CONTINUE_MSG(skeleton == nullptr, vformat("Unable to find Skeleton for node %d skin %d", node_i, skin_i));
mi->get_parent()->remove_child(mi);
- skeleton->add_child(mi);
+ skeleton->add_child(mi, true);
mi->set_owner(skeleton->get_owner());
mi->set_skin(state->skins.write[skin_i]->godot_skin);
@@ -6003,7 +6233,7 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_roo
}
}
-GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, Transform3D p_bone_rest, int32_t p_track_i, GLTFNodeIndex p_node_i) {
+GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i) {
Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
@@ -6016,44 +6246,45 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
}
Animation::TrackType track_type = p_animation->track_get_type(p_track_i);
int32_t key_count = p_animation->track_get_key_count(p_track_i);
- Vector<float> times;
+ Vector<real_t> times;
times.resize(key_count);
String path = p_animation->track_get_path(p_track_i);
for (int32_t key_i = 0; key_i < key_count; key_i++) {
times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
}
- const float BAKE_FPS = 30.0f;
- if (track_type == Animation::TYPE_TRANSFORM3D) {
- p_track.translation_track.times = times;
- p_track.translation_track.interpolation = gltf_interpolation;
- p_track.rotation_track.times = times;
- p_track.rotation_track.interpolation = gltf_interpolation;
+ if (track_type == Animation::TYPE_SCALE_3D) {
p_track.scale_track.times = times;
p_track.scale_track.interpolation = gltf_interpolation;
-
p_track.scale_track.values.resize(key_count);
- p_track.scale_track.interpolation = gltf_interpolation;
- p_track.translation_track.values.resize(key_count);
- p_track.translation_track.interpolation = gltf_interpolation;
- p_track.rotation_track.values.resize(key_count);
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Vector3 scale;
+ Error err = p_animation->scale_track_get_key(p_track_i, key_i, &scale);
+ ERR_CONTINUE(err != OK);
+ p_track.scale_track.values.write[key_i] = scale;
+ }
+ } else if (track_type == Animation::TYPE_POSITION_3D) {
+ p_track.position_track.times = times;
+ p_track.position_track.values.resize(key_count);
+ p_track.position_track.interpolation = gltf_interpolation;
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Vector3 position;
+ Error err = p_animation->position_track_get_key(p_track_i, key_i, &position);
+ ERR_CONTINUE(err != OK);
+ p_track.position_track.values.write[key_i] = position;
+ }
+ } else if (track_type == Animation::TYPE_ROTATION_3D) {
+ p_track.rotation_track.times = times;
p_track.rotation_track.interpolation = gltf_interpolation;
+ p_track.rotation_track.values.resize(key_count);
for (int32_t key_i = 0; key_i < key_count; key_i++) {
- Vector3 translation;
Quaternion rotation;
- Vector3 scale;
- Error err = p_animation->transform_track_get_key(p_track_i, key_i, &translation, &rotation, &scale);
+ Error err = p_animation->rotation_track_get_key(p_track_i, key_i, &rotation);
ERR_CONTINUE(err != OK);
- Transform3D xform;
- xform.basis.set_quaternion_scale(rotation, scale);
- xform.origin = translation;
- xform = p_bone_rest * xform;
- p_track.translation_track.values.write[key_i] = xform.get_origin();
- p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion();
- p_track.scale_track.values.write[key_i] = xform.basis.get_scale();
+ p_track.rotation_track.values.write[key_i] = rotation;
}
} else if (path.find(":transform") != -1) {
- p_track.translation_track.times = times;
- p_track.translation_track.interpolation = gltf_interpolation;
+ p_track.position_track.times = times;
+ p_track.position_track.interpolation = gltf_interpolation;
p_track.rotation_track.times = times;
p_track.rotation_track.interpolation = gltf_interpolation;
p_track.scale_track.times = times;
@@ -6061,13 +6292,13 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
p_track.scale_track.values.resize(key_count);
p_track.scale_track.interpolation = gltf_interpolation;
- p_track.translation_track.values.resize(key_count);
- p_track.translation_track.interpolation = gltf_interpolation;
+ p_track.position_track.values.resize(key_count);
+ p_track.position_track.interpolation = gltf_interpolation;
p_track.rotation_track.values.resize(key_count);
p_track.rotation_track.interpolation = gltf_interpolation;
for (int32_t key_i = 0; key_i < key_count; key_i++) {
Transform3D xform = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.translation_track.values.write[key_i] = xform.get_origin();
+ p_track.position_track.values.write[key_i] = xform.get_origin();
p_track.rotation_track.values.write[key_i] = xform.basis.get_rotation_quaternion();
p_track.scale_track.values.write[key_i] = xform.basis.get_scale();
}
@@ -6083,16 +6314,16 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Quaternion rotation_track = p_animation->track_get_key_value(p_track_i, key_i);
p_track.rotation_track.values.write[key_i] = rotation_track;
}
- } else if (path.find(":translation") != -1) {
- p_track.translation_track.times = times;
- p_track.translation_track.interpolation = gltf_interpolation;
+ } else if (path.find(":position") != -1) {
+ p_track.position_track.times = times;
+ p_track.position_track.interpolation = gltf_interpolation;
- p_track.translation_track.values.resize(key_count);
- p_track.translation_track.interpolation = gltf_interpolation;
+ p_track.position_track.values.resize(key_count);
+ p_track.position_track.interpolation = gltf_interpolation;
for (int32_t key_i = 0; key_i < key_count; key_i++) {
- Vector3 translation = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.translation_track.values.write[key_i] = translation;
+ Vector3 position = p_animation->track_get_key_value(p_track_i, key_i);
+ p_track.position_track.values.write[key_i] = position;
}
} else if (path.find(":rotation") != -1) {
p_track.rotation_track.times = times;
@@ -6121,7 +6352,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
if (path.find("/scale") != -1) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
if (!p_track.scale_track.times.size()) {
- Vector<float> new_times;
+ Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
new_times.write[key_i] = key_i / BAKE_FPS;
@@ -6141,48 +6372,41 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
Vector3 bezier_track = p_track.scale_track.values[key_i];
if (path.find("/scale:x") != -1) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.x = p_bone_rest.affine_inverse().basis.get_scale().x * bezier_track.x;
} else if (path.find("/scale:y") != -1) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.y = p_bone_rest.affine_inverse().basis.get_scale().y * bezier_track.y;
} else if (path.find("/scale:z") != -1) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.z = p_bone_rest.affine_inverse().basis.get_scale().z * bezier_track.z;
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
- } else if (path.find("/translation") != -1) {
+ } else if (path.find("/position") != -1) {
const int32_t keys = p_animation->track_get_key_time(p_track_i, key_count - 1) * BAKE_FPS;
- if (!p_track.translation_track.times.size()) {
- Vector<float> new_times;
+ if (!p_track.position_track.times.size()) {
+ Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
new_times.write[key_i] = key_i / BAKE_FPS;
}
- p_track.translation_track.times = new_times;
- p_track.translation_track.interpolation = gltf_interpolation;
+ p_track.position_track.times = new_times;
+ p_track.position_track.interpolation = gltf_interpolation;
- p_track.translation_track.values.resize(keys);
- p_track.translation_track.interpolation = gltf_interpolation;
+ p_track.position_track.values.resize(keys);
+ p_track.position_track.interpolation = gltf_interpolation;
}
for (int32_t key_i = 0; key_i < keys; key_i++) {
- Vector3 bezier_track = p_track.translation_track.values[key_i];
- if (path.find("/translation:x") != -1) {
+ Vector3 bezier_track = p_track.position_track.values[key_i];
+ if (path.find("/position:x") != -1) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.x = p_bone_rest.affine_inverse().origin.x * bezier_track.x;
- } else if (path.find("/translation:y") != -1) {
+ } else if (path.find("/position:y") != -1) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.y = p_bone_rest.affine_inverse().origin.y * bezier_track.y;
- } else if (path.find("/translation:z") != -1) {
+ } else if (path.find("/position:z") != -1) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
- bezier_track.z = p_bone_rest.affine_inverse().origin.z * bezier_track.z;
}
- p_track.translation_track.values.write[key_i] = bezier_track;
+ p_track.position_track.values.write[key_i] = bezier_track;
}
}
}
-
return p_track;
}
@@ -6197,19 +6421,19 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
continue;
}
String orig_track_path = animation->track_get_path(track_i);
- if (String(orig_track_path).find(":translation") != -1) {
- const Vector<String> node_suffix = String(orig_track_path).split(":translation");
+ if (String(orig_track_path).find(":position") != -1) {
+ const Vector<String> node_suffix = String(orig_track_path).split(":position");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
- for (Map<GLTFNodeIndex, Node *>::Element *translation_scene_node_i = state->scene_nodes.front(); translation_scene_node_i; translation_scene_node_i = translation_scene_node_i->next()) {
- if (translation_scene_node_i->get() == node) {
- GLTFNodeIndex node_index = translation_scene_node_i->key();
- Map<int, GLTFAnimation::Track>::Element *translation_track_i = gltf_animation->get_tracks().find(node_index);
+ 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);
GLTFAnimation::Track track;
- if (translation_track_i) {
- track = translation_track_i->get();
+ if (position_track_i) {
+ track = position_track_i->get();
}
- track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index);
+ track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
@@ -6217,15 +6441,15 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const Vector<String> node_suffix = String(orig_track_path).split(":rotation_degrees");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
- for (Map<GLTFNodeIndex, Node *>::Element *rotation_degree_scene_node_i = state->scene_nodes.front(); rotation_degree_scene_node_i; rotation_degree_scene_node_i = rotation_degree_scene_node_i->next()) {
- if (rotation_degree_scene_node_i->get() == node) {
- GLTFNodeIndex node_index = rotation_degree_scene_node_i->key();
+ 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);
GLTFAnimation::Track track;
if (rotation_degree_track_i) {
track = rotation_degree_track_i->get();
}
- track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index);
+ track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
@@ -6233,15 +6457,15 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const Vector<String> node_suffix = String(orig_track_path).split(":scale");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
- for (Map<GLTFNodeIndex, Node *>::Element *scale_scene_node_i = state->scene_nodes.front(); scale_scene_node_i; scale_scene_node_i = scale_scene_node_i->next()) {
- if (scale_scene_node_i->get() == node) {
- GLTFNodeIndex node_index = scale_scene_node_i->key();
+ 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);
GLTFAnimation::Track track;
if (scale_track_i) {
track = scale_track_i->get();
}
- track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index);
+ track = _convert_animation_track(state, track, animation, track_i, node_index);
gltf_animation->get_tracks().insert(node_index, track);
}
}
@@ -6249,80 +6473,69 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const Vector<String> node_suffix = String(orig_track_path).split(":transform");
const NodePath path = node_suffix[0];
const Node *node = ap->get_parent()->get_node_or_null(path);
- for (Map<GLTFNodeIndex, Node *>::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) {
- if (transform_track_i->get() == node) {
+ for (const KeyValue<GLTFNodeIndex, Node *> &transform_track_i : state->scene_nodes) {
+ if (transform_track_i.value == node) {
GLTFAnimation::Track track;
- track = _convert_animation_track(state, track, animation, Transform3D(), track_i, transform_track_i->key());
- gltf_animation->get_tracks().insert(transform_track_i->key(), track);
+ track = _convert_animation_track(state, track, animation, track_i, transform_track_i.key);
+ gltf_animation->get_tracks().insert(transform_track_i.key, track);
}
}
} else if (String(orig_track_path).find(":blend_shapes/") != -1) {
const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
const NodePath path = node_suffix[0];
const String suffix = node_suffix[1];
- const Node *node = ap->get_parent()->get_node_or_null(path);
- for (Map<GLTFNodeIndex, Node *>::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) {
- if (transform_track_i->get() == node) {
- const MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(node);
- if (!mi) {
- continue;
- }
- Ref<ArrayMesh> array_mesh = mi->get_mesh();
- if (array_mesh.is_null()) {
+ Node *node = ap->get_parent()->get_node_or_null(path);
+ MeshInstance3D *mi = cast_to<MeshInstance3D>(node);
+ Ref<Mesh> mesh = mi->get_mesh();
+ ERR_CONTINUE(mesh.is_null());
+ int32_t mesh_index = -1;
+ for (const KeyValue<GLTFNodeIndex, Node *> &mesh_track_i : state->scene_nodes) {
+ if (mesh_track_i.value == node) {
+ mesh_index = mesh_track_i.key;
+ }
+ }
+ ERR_CONTINUE(mesh_index == -1);
+ Map<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++) {
+ String shape_name = mesh->get_blend_shape_name(shape_i);
+ NodePath shape_path = String(path) + ":" + shape_name;
+ int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE);
+ if (shape_track_i == -1) {
+ GLTFAnimation::Channel<real_t> weight;
+ weight.interpolation = GLTFAnimation::INTERP_LINEAR;
+ weight.times.push_back(0.0f);
+ weight.times.push_back(0.0f);
+ weight.values.push_back(0.0f);
+ weight.values.push_back(0.0f);
+ track.weight_tracks.push_back(weight);
continue;
}
- if (node_suffix.size() != 2) {
- continue;
+ Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i);
+ GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
+ gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
+ gltf_interpolation = GLTFAnimation::INTERP_STEP;
+ } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
+ gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
}
- GLTFNodeIndex mesh_index = -1;
- for (GLTFNodeIndex node_i = 0; node_i < state->scene_nodes.size(); node_i++) {
- if (state->scene_nodes[node_i] == node) {
- mesh_index = node_i;
- break;
- }
+ int32_t key_count = animation->track_get_key_count(shape_track_i);
+ GLTFAnimation::Channel<real_t> weight;
+ weight.interpolation = gltf_interpolation;
+ weight.times.resize(key_count);
+ for (int32_t time_i = 0; time_i < key_count; time_i++) {
+ weight.times.write[time_i] = animation->track_get_key_time(shape_track_i, time_i);
}
- ERR_CONTINUE(mesh_index == -1);
- Ref<Mesh> mesh = mi->get_mesh();
- ERR_CONTINUE(mesh.is_null());
- for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
- if (mesh->get_blend_shape_name(shape_i) != suffix) {
- continue;
- }
- GLTFAnimation::Track track;
- Map<int, GLTFAnimation::Track>::Element *blend_shape_track_i = gltf_animation->get_tracks().find(mesh_index);
- if (blend_shape_track_i) {
- track = blend_shape_track_i->get();
- }
- Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i);
-
- GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
- gltf_interpolation = GLTFAnimation::INTERP_STEP;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
- gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
- }
- Animation::TrackType track_type = animation->track_get_type(track_i);
- if (track_type == Animation::TYPE_VALUE) {
- int32_t key_count = animation->track_get_key_count(track_i);
- GLTFAnimation::Channel<float> weight;
- weight.interpolation = gltf_interpolation;
- weight.times.resize(key_count);
- for (int32_t time_i = 0; time_i < key_count; time_i++) {
- weight.times.write[time_i] = animation->track_get_key_time(track_i, time_i);
- }
- weight.values.resize(key_count);
- for (int32_t value_i = 0; value_i < key_count; value_i++) {
- weight.values.write[value_i] = animation->track_get_key_value(track_i, value_i);
- }
- track.weight_tracks.push_back(weight);
- }
- gltf_animation->get_tracks()[mesh_index] = track;
+ weight.values.resize(key_count);
+ for (int32_t value_i = 0; value_i < key_count; value_i++) {
+ weight.values.write[value_i] = animation->track_get_key_value(shape_track_i, value_i);
}
+ track.weight_tracks.push_back(weight);
}
+ tracks[mesh_index] = track;
}
-
} else if (String(orig_track_path).find(":") != -1) {
//Process skeleton
const Vector<String> node_suffix = String(orig_track_path).split(":");
@@ -6340,7 +6553,6 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
Ref<GLTFSkeleton> skeleton_gltf = state->skeletons[skeleton_gltf_i];
int32_t bone = skeleton->find_bone(suffix);
ERR_CONTINUE(bone == -1);
- Transform3D xform = skeleton->get_bone_rest(bone);
if (!skeleton_gltf->godot_bone_node.has(bone)) {
continue;
}
@@ -6350,27 +6562,24 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (property_track_i) {
track = property_track_i->get();
}
- track = _convert_animation_track(state, track, animation, xform, track_i, node_i);
+ track = _convert_animation_track(state, track, animation, track_i, node_i);
gltf_animation->get_tracks()[node_i] = track;
}
}
} else if (String(orig_track_path).find(":") == -1) {
ERR_CONTINUE(!ap->get_parent());
- for (int32_t node_i = 0; node_i < ap->get_parent()->get_child_count(); node_i++) {
- const Node *child = ap->get_parent()->get_child(node_i);
- const Node *node = child->get_node_or_null(orig_track_path);
- for (Map<GLTFNodeIndex, Node *>::Element *scene_node_i = state->scene_nodes.front(); scene_node_i; scene_node_i = scene_node_i->next()) {
- if (scene_node_i->get() == node) {
- GLTFNodeIndex node_index = scene_node_i->key();
- Map<int, GLTFAnimation::Track>::Element *node_track_i = gltf_animation->get_tracks().find(node_index);
- GLTFAnimation::Track track;
- if (node_track_i) {
- track = node_track_i->get();
- }
- track = _convert_animation_track(state, track, animation, Transform3D(), track_i, node_index);
- gltf_animation->get_tracks().insert(node_index, track);
- break;
+ Node *godot_node = ap->get_parent()->get_node_or_null(orig_track_path);
+ 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);
+ GLTFAnimation::Track track;
+ if (node_track_i) {
+ track = node_track_i->get();
}
+ track = _convert_animation_track(state, track, animation, track_i, node_i);
+ gltf_animation->get_tracks()[node_i] = track;
+ break;
}
}
}
@@ -6588,33 +6797,36 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
const uint32_t magic = 0x46546C67; // GLTF
const int32_t header_size = 12;
const int32_t chunk_header_size = 8;
-
- for (int32_t pad_i = 0; pad_i < (chunk_header_size + json.utf8().length()) % 4; pad_i++) {
- json += " ";
- }
CharString cs = json.utf8();
- const uint32_t text_chunk_length = cs.length();
-
+ const uint32_t text_data_length = cs.length();
+ const uint32_t text_chunk_length = ((text_data_length + 3) & (~3));
const uint32_t text_chunk_type = 0x4E4F534A; //JSON
- int32_t binary_data_length = 0;
+
+ uint32_t binary_data_length = 0;
if (state->buffers.size()) {
binary_data_length = state->buffers[0].size();
}
- const int32_t binary_chunk_length = binary_data_length;
- const int32_t binary_chunk_type = 0x004E4942; //BIN
+ const uint32_t binary_chunk_length = ((binary_data_length + 3) & (~3));
+ const uint32_t binary_chunk_type = 0x004E4942; //BIN
f->create(FileAccess::ACCESS_RESOURCES);
f->store_32(magic);
f->store_32(state->major_version); // version
- f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_data_length); // length
+ f->store_32(header_size + chunk_header_size + text_chunk_length + chunk_header_size + binary_chunk_length); // length
f->store_32(text_chunk_length);
f->store_32(text_chunk_type);
f->store_buffer((uint8_t *)&cs[0], cs.length());
+ for (uint32_t pad_i = text_data_length; pad_i < text_chunk_length; pad_i++) {
+ f->store_8(' ');
+ }
if (binary_chunk_length) {
f->store_32(binary_chunk_length);
f->store_32(binary_chunk_type);
f->store_buffer(state->buffers[0].ptr(), binary_data_length);
}
+ for (uint32_t pad_i = binary_data_length; pad_i < binary_chunk_length; pad_i++) {
+ f->store_8(0);
+ }
f->close();
} else {
@@ -6630,3 +6842,138 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
}
return err;
}
+
+Error GLTFDocument::save_scene(Node *p_node, const String &p_path,
+ const String &p_src_path, uint32_t p_flags,
+ float p_bake_fps, Ref<GLTFState> r_state) {
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+
+ Ref<GLTFDocument> gltf_document;
+ gltf_document.instantiate();
+ 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(this, p_node);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+
+ if (r_state == Ref<GLTFState>()) {
+ r_state.instantiate();
+ }
+ Error err = gltf_document->serialize(r_state, p_node, p_path);
+ 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->export_post(this);
+ ERR_FAIL_COND_V(err != OK, err);
+ }
+ return OK;
+}
+
+Node *GLTFDocument::import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err) {
+ // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
+ if (r_state == Ref<GLTFState>()) {
+ r_state.instantiate();
+ }
+ r_state->use_named_skin_binds =
+ p_flags & EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
+
+ Ref<GLTFDocument> gltf_document;
+ gltf_document.instantiate();
+ 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_preflight(this);
+ if (r_err) {
+ *r_err = err;
+ }
+ ERR_FAIL_COND_V(err != OK, nullptr);
+ }
+ Error err = gltf_document->parse(r_state, p_path);
+ if (r_err) {
+ *r_err = err;
+ }
+ ERR_FAIL_COND_V(err != Error::OK, nullptr);
+
+ Node3D *root = memnew(Node3D);
+ for (int32_t root_i = 0; root_i < r_state->root_nodes.size(); root_i++) {
+ gltf_document->_generate_scene_node(r_state, root, root, r_state->root_nodes[root_i]);
+ }
+ gltf_document->_process_mesh_instances(r_state, root);
+ if (r_state->animations.size()) {
+ AnimationPlayer *ap = memnew(AnimationPlayer);
+ root->add_child(ap, true);
+ ap->set_owner(root);
+ for (int i = 0; i < r_state->animations.size(); i++) {
+ gltf_document->_import_animation(r_state, ap, i, p_bake_fps);
+ }
+ }
+ 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(this, root);
+ if (r_err) {
+ *r_err = err;
+ }
+ ERR_FAIL_COND_V(err != OK, nullptr);
+ }
+ return root;
+}
+
+void GLTFDocument::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("save_scene", "node", "path", "src_path", "flags", "bake_fps", "state"),
+ &GLTFDocument::save_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>()));
+ ClassDB::bind_method(D_METHOD("import_scene", "path", "flags", "bake_fps", "state"),
+ &GLTFDocument::import_scene, DEFVAL(0), DEFVAL(30), DEFVAL(Ref<GLTFState>()));
+ ClassDB::bind_method(D_METHOD("set_extensions", "extensions"),
+ &GLTFDocument::set_extensions);
+ ClassDB::bind_method(D_METHOD("get_extensions"),
+ &GLTFDocument::get_extensions);
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "extensions", PROPERTY_HINT_ARRAY_TYPE,
+ vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "GLTFDocumentExtension"),
+ PROPERTY_USAGE_DEFAULT),
+ "set_extensions", "get_extensions");
+}
+
+void GLTFDocument::_build_parent_hierachy(Ref<GLTFState> state) {
+ // build the hierarchy
+ for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
+ for (int j = 0; j < state->nodes[node_i]->children.size(); j++) {
+ GLTFNodeIndex child_i = state->nodes[node_i]->children[j];
+ ERR_FAIL_INDEX(child_i, state->nodes.size());
+ if (state->nodes.write[child_i]->parent != -1) {
+ continue;
+ }
+ state->nodes.write[child_i]->parent = node_i;
+ }
+ }
+}
+
+Node *GLTFDocument::import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state) {
+ Error err = FAILED;
+ List<String> deps;
+ Node *node = import_scene_gltf(p_path, p_flags, p_bake_fps, r_state, &deps, &err);
+ if (err != OK) {
+ return nullptr;
+ }
+ return node;
+}
+
+void GLTFDocument::set_extensions(TypedArray<GLTFDocumentExtension> p_extensions) {
+ document_extensions = p_extensions;
+}
+
+TypedArray<GLTFDocumentExtension> GLTFDocument::get_extensions() const {
+ return document_extensions;
+}
+
+GLTFDocument::GLTFDocument() {
+ bool is_editor = ::Engine::get_singleton()->is_editor_hint();
+ if (is_editor) {
+ return;
+ }
+ Ref<GLTFDocumentExtensionConvertImporterMesh> extension_editor;
+ extension_editor.instantiate();
+ document_extensions.push_back(extension_editor);
+}
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 7a826897a9..f8d9521733 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -33,8 +33,11 @@
#include "gltf_animation.h"
-#include "editor/import/scene_importer_mesh_node_3d.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"
@@ -43,13 +46,19 @@
#include "scene/resources/material.h"
#include "scene/resources/texture.h"
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
+#include <cstdint>
class GLTFState;
class GLTFSkin;
class GLTFNode;
class GLTFSpecGloss;
class GLTFSkeleton;
+class CSGShape3D;
+class GridMap;
+class MultiMeshInstance3D;
+class GLTFDocumentExtension;
using GLTFAccessorIndex = int;
using GLTFAnimationIndex = int;
@@ -70,8 +79,13 @@ class GLTFDocument : public Resource {
friend class GLTFState;
friend class GLTFSkin;
friend class GLTFSkeleton;
+ TypedArray<GLTFDocumentExtension> document_extensions;
+
+private:
+ const float BAKE_FPS = 30.0f;
public:
+ GLTFDocument();
const int32_t JOINT_GROUP_SIZE = 4;
enum GLTFType {
TYPE_SCALAR,
@@ -102,6 +116,18 @@ public:
COMPONENT_TYPE_FLOAT = 5126,
};
+protected:
+ static void _bind_methods();
+
+public:
+ Node *import_scene(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state);
+ Node *import_scene_gltf(const String &p_path, uint32_t p_flags, int32_t p_bake_fps, Ref<GLTFState> r_state, List<String> *r_missing_deps, Error *r_err = nullptr);
+ Error save_scene(Node *p_node, const String &p_path,
+ const String &p_src_path, uint32_t p_flags,
+ float p_bake_fps, Ref<GLTFState> r_state);
+ void set_extensions(TypedArray<GLTFDocumentExtension> p_extensions);
+ TypedArray<GLTFDocumentExtension> get_extensions() const;
+
private:
template <class T>
static Array to_array(const Vector<T> &p_inp) {
@@ -155,6 +181,7 @@ private:
r_out[keys[i]] = p_inp[keys[i]];
}
}
+ void _build_parent_hierachy(Ref<GLTFState> state);
double _filter_number(double p_float);
String _get_component_type_name(const uint32_t p_component);
int _get_component_type_size(const int component_type);
@@ -243,8 +270,6 @@ private:
Error _reparent_non_joint_skeleton_subtrees(
Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
const Vector<GLTFNodeIndex> &non_joints);
- Error _reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
- const GLTFNodeIndex node_index);
Error _determine_skeleton_roots(Ref<GLTFState> state,
const GLTFSkeletonIndex skel_i);
Error _create_skeletons(Ref<GLTFState> state);
@@ -262,15 +287,13 @@ private:
Skeleton3D *skeleton,
const GLTFNodeIndex node_index,
const GLTFNodeIndex bone_index);
- EditorSceneImporterMeshNode3D *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
- Camera3D *_generate_camera(Ref<GLTFState> state, Node *scene_parent,
- const GLTFNodeIndex node_index);
- Node3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
- Node3D *_generate_spatial(Ref<GLTFState> state, Node *scene_parent,
- const GLTFNodeIndex node_index);
+ ImporterMeshInstance3D *_generate_mesh_instance(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index);
+ Camera3D *_generate_camera(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index);
+ Node3D *_generate_light(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index);
+ Node3D *_generate_spatial(Ref<GLTFState> state, Node *parent_node, const GLTFNodeIndex node_index);
void _assign_scene_names(Ref<GLTFState> state);
template <class T>
- T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values,
+ T _interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values,
const float p_time,
const GLTFAnimation::Interpolation p_interp);
GLTFAccessorIndex _encode_accessor_as_quaternions(Ref<GLTFState> state,
@@ -333,12 +356,11 @@ private:
String interpolation_to_string(const GLTFAnimation::Interpolation p_interp);
GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> state,
GLTFAnimation::Track p_track,
- Ref<Animation> p_animation, Transform3D p_bone_rest,
+ Ref<Animation> p_animation,
int32_t p_track_i,
GLTFNodeIndex p_node_i);
Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path);
Error _encode_buffer_glb(Ref<GLTFState> state, const String &p_path);
- Error _serialize_bone_attachment(Ref<GLTFState> state);
Dictionary _serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material);
Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material);
Error _serialize_version(Ref<GLTFState> state);
@@ -369,20 +391,17 @@ public:
void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index);
void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const GLTFAnimationIndex index, const int bake_fps);
- GLTFMeshIndex _convert_mesh_instance(Ref<GLTFState> state,
- MeshInstance3D *p_mesh_instance);
void _convert_mesh_instances(Ref<GLTFState> state);
GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera3D *p_camera);
- void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Node3D *spatial, Ref<GLTFNode> gltf_node);
+ void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node);
GLTFLightIndex _convert_light(Ref<GLTFState> state, Light3D *p_light);
- GLTFSkeletonIndex _convert_skeleton(Ref<GLTFState> state, Skeleton3D *p_skeleton);
void _convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node);
- void _convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root,
+ void _convert_scene_node(Ref<GLTFState> state, Node *p_current,
const GLTFNodeIndex p_gltf_current,
const GLTFNodeIndex p_gltf_root);
#ifdef MODULE_CSG_ENABLED
- void _convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
+ void _convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
#endif // MODULE_CSG_ENABLED
void _create_gltf_node(Ref<GLTFState> state,
@@ -393,40 +412,39 @@ public:
Ref<GLTFNode> gltf_node);
void _convert_animation_player_to_gltf(
AnimationPlayer *animation_player, Ref<GLTFState> state,
- const GLTFNodeIndex &p_gltf_current,
- const GLTFNodeIndex &p_gltf_root_index,
- Ref<GLTFNode> p_gltf_node, Node *p_scene_parent,
- Node *p_root);
+ GLTFNodeIndex p_gltf_current,
+ GLTFNodeIndex p_gltf_root_index,
+ Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
void _check_visibility(Node *p_node, bool &retflag);
void _convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state,
- Node3D *spatial,
Ref<GLTFNode> gltf_node);
#ifdef MODULE_GRIDMAP_ENABLED
void _convert_grid_map_to_gltf(
- Node *p_scene_parent,
- const GLTFNodeIndex &p_parent_node_index,
- const GLTFNodeIndex &p_root_node_index,
- Ref<GLTFNode> gltf_node, Ref<GLTFState> state,
- Node *p_root_node);
+ GridMap *p_grid_map,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_node_index,
+ Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
#endif // MODULE_GRIDMAP_ENABLED
- void _convert_mult_mesh_instance_to_gltf(
- Node *p_scene_parent,
- const GLTFNodeIndex &p_parent_node_index,
- const GLTFNodeIndex &p_root_node_index,
- Ref<GLTFNode> gltf_node, Ref<GLTFState> state,
- Node *p_root_node);
+ void _convert_multi_mesh_instance_to_gltf(
+ MultiMeshInstance3D *p_multi_mesh_instance,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_node_index,
+ Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
void _convert_skeleton_to_gltf(
- Node *p_scene_parent, Ref<GLTFState> state,
- const GLTFNodeIndex &p_parent_node_index,
- const GLTFNodeIndex &p_root_node_index,
- Ref<GLTFNode> gltf_node, Node *p_root_node);
- void _convert_bone_attachment_to_gltf(Node *p_scene_parent,
+ Skeleton3D *p_scene_parent, Ref<GLTFState> state,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_node_index,
+ Ref<GLTFNode> gltf_node);
+ void _convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_attachment,
Ref<GLTFState> state,
- Ref<GLTFNode> gltf_node,
- bool &retflag);
- void _convert_mesh_to_gltf(Node *p_scene_parent,
- Ref<GLTFState> state, Node3D *spatial,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_node_index,
Ref<GLTFNode> gltf_node);
+ void _convert_mesh_instance_to_gltf(MeshInstance3D *p_mesh_instance,
+ Ref<GLTFState> state,
+ Ref<GLTFNode> gltf_node);
+ GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> state,
+ MeshInstance3D *p_mesh_instance);
void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
String p_animation_track_name);
Error serialize(Ref<GLTFState> state, Node *p_root, const String &p_path);
diff --git a/modules/gdnative/net/webrtc_gdnative.cpp b/modules/gltf/gltf_document_extension.cpp
index 76ccbad009..a423059a9c 100644
--- a/modules/gdnative/net/webrtc_gdnative.cpp
+++ b/modules/gltf/gltf_document_extension.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* webrtc_gdnative.cpp */
+/* gltf_document_extension.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,33 +28,61 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#include "gltf_document_extension.h"
-#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "modules/webrtc/webrtc_data_channel_gdnative.h"
-#include "modules/webrtc/webrtc_peer_connection_gdnative.h"
-#endif
+#include "gltf_document.h"
-extern "C" {
+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);
+}
+
+Array GLTFDocumentExtension::get_import_setting_keys() const {
+ return import_settings.keys();
+}
-void GDAPI godot_net_bind_webrtc_peer_connection(godot_object *p_obj, const godot_net_webrtc_peer_connection *p_impl) {
-#ifdef WEBRTC_GDNATIVE_ENABLED
- ((WebRTCPeerConnectionGDNative *)p_obj)->set_native_webrtc_peer_connection(p_impl);
-#endif
+Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const {
+ if (!import_settings.has(p_key)) {
+ return Variant();
+ }
+ return import_settings[p_key];
}
-void GDAPI godot_net_bind_webrtc_data_channel(godot_object *p_obj, const godot_net_webrtc_data_channel *p_impl) {
-#ifdef WEBRTC_GDNATIVE_ENABLED
- ((WebRTCDataChannelGDNative *)p_obj)->set_native_webrtc_data_channel(p_impl);
-#endif
+void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) {
+ import_settings[p_key] = p_var;
}
-godot_error GDAPI godot_net_set_webrtc_library(const godot_net_webrtc_library *p_lib) {
-#ifdef WEBRTC_GDNATIVE_ENABLED
- return (godot_error)WebRTCPeerConnectionGDNative::set_default_library(p_lib);
-#else
- return (godot_error)ERR_UNAVAILABLE;
-#endif
+Array GLTFDocumentExtension::get_export_setting_keys() const {
+ return import_settings.keys();
}
+
+Variant GLTFDocumentExtension::get_export_setting(const StringName &p_key) const {
+ if (!import_settings.has(p_key)) {
+ return Variant();
+ }
+ return import_settings[p_key];
+}
+
+void GLTFDocumentExtension::set_export_setting(const StringName &p_key, Variant p_var) {
+ import_settings[p_key] = p_var;
}
diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gltf/gltf_document_extension.h
index 29013f9367..622a65708c 100644
--- a/modules/gdnative/net/packet_peer_gdnative.h
+++ b/modules/gltf/gltf_document_extension.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* packet_peer_gdnative.h */
+/* gltf_document_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,32 +28,36 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PACKET_PEER_GDNATIVE_H
-#define PACKET_PEER_GDNATIVE_H
+#ifndef GLTF_DOCUMENT_EXTENSION_H
+#define GLTF_DOCUMENT_EXTENSION_H
-#include "core/io/packet_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#include "core/io/resource.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/typed_array.h"
+#include "core/variant/variant.h"
+class GLTFDocument;
+class GLTFDocumentExtension : public Resource {
+ GDCLASS(GLTFDocumentExtension, Resource);
-class PacketPeerGDNative : public PacketPeer {
- GDCLASS(PacketPeerGDNative, PacketPeer);
+ Dictionary import_settings;
+ Dictionary export_settings;
protected:
static void _bind_methods();
- const godot_net_packet_peer *interface;
public:
- PacketPeerGDNative();
- ~PacketPeerGDNative();
+ 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; }
- /* Sets the interface implementation from GDNative */
- void set_native_packet_peer(const godot_net_packet_peer *p_impl);
-
- /* Specific to PacketPeer */
- virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override;
- virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
- virtual int get_max_packet_size() const override;
- virtual int get_available_packet_count() const override;
+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; }
};
-#endif // PACKET_PEER_GDNATIVE_H
+#endif // GLTF_DOCUMENT_EXTENSION_H
diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
index 3bcdfed8ff..56c8f5ca27 100644
--- a/modules/gdnative/net/packet_peer_gdnative.cpp
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* packet_peer_gdnative.cpp */
+/* gltf_document_extension_convert_importer_mesh.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,45 +28,55 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "packet_peer_gdnative.h"
+#include "gltf_document_extension_convert_importer_mesh.h"
+#include "core/error/error_macros.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/resources/importer_mesh.h"
-PacketPeerGDNative::PacketPeerGDNative() {
- interface = nullptr;
-}
-
-PacketPeerGDNative::~PacketPeerGDNative() {
-}
-
-void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) {
- interface = p_impl;
-}
-
-void PacketPeerGDNative::_bind_methods() {
-}
+#include <cstddef>
-Error PacketPeerGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size);
+void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
}
-Error PacketPeerGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
-}
-
-int PacketPeerGDNative::get_max_packet_size() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_max_packet_size(interface->data);
-}
-
-int PacketPeerGDNative::get_available_packet_count() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_packet_count(interface->data);
-}
-
-extern "C" {
-
-void GDAPI godot_net_bind_packet_peer(godot_object *p_obj, const godot_net_packet_peer *p_impl) {
- ((PacketPeerGDNative *)p_obj)->set_native_packet_peer(p_impl);
-}
+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);
+ List<Node *> queue;
+ queue.push_back(p_node);
+ 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);
+ }
+ }
+ }
+ int child_count = node->get_child_count();
+ for (int i = 0; i < child_count; i++) {
+ queue.push_back(node->get_child(i));
+ }
+ queue.pop_front();
+ }
+ while (!queue.is_empty()) {
+ List<Node *>::Element *E = delete_queue.front();
+ Node *node = E->get();
+ memdelete(node);
+ delete_queue.pop_front();
+ }
+ return OK;
}
diff --git a/modules/webm/register_types.cpp b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
index 8f690a6892..85ddb4d250 100644
--- a/modules/webm/register_types.cpp
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.cpp */
+/* gltf_document_extension_convert_importer_mesh.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,20 +28,28 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "register_types.h"
+#ifndef GLTF_EXTENSION_EDITOR_H
+#define GLTF_EXTENSION_EDITOR_H
-#include "video_stream_webm.h"
+#include "core/io/resource.h"
+#include "core/variant/dictionary.h"
-static Ref<ResourceFormatLoaderWebm> resource_loader_webm;
+#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"
-void register_webm_types() {
- resource_loader_webm.instantiate();
- ResourceLoader::add_resource_format_loader(resource_loader_webm, true);
+class GLTFDocumentExtension;
+class GLTFDocument;
+class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
+ GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
- GDREGISTER_CLASS(VideoStreamWebm);
-}
+protected:
+ static void _bind_methods();
-void unregister_webm_types() {
- ResourceLoader::remove_resource_format_loader(resource_loader_webm);
- resource_loader_webm.unref();
-}
+public:
+ Error import_post(Ref<GLTFDocument> p_document, Node *p_node) override;
+};
+#endif // GLTF_EXTENSION_EDITOR_H
diff --git a/modules/gltf/gltf_light.cpp b/modules/gltf/gltf_light.cpp
index 95cca9cf71..c5aa8d5724 100644
--- a/modules/gltf/gltf_light.cpp
+++ b/modules/gltf/gltf_light.cpp
@@ -35,8 +35,8 @@ void GLTFLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color);
ClassDB::bind_method(D_METHOD("get_intensity"), &GLTFLight::get_intensity);
ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &GLTFLight::set_intensity);
- ClassDB::bind_method(D_METHOD("get_type"), &GLTFLight::get_type);
- ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFLight::set_type);
+ ClassDB::bind_method(D_METHOD("get_light_type"), &GLTFLight::get_light_type);
+ ClassDB::bind_method(D_METHOD("set_light_type", "light_type"), &GLTFLight::set_light_type);
ClassDB::bind_method(D_METHOD("get_range"), &GLTFLight::get_range);
ClassDB::bind_method(D_METHOD("set_range", "range"), &GLTFLight::set_range);
ClassDB::bind_method(D_METHOD("get_inner_cone_angle"), &GLTFLight::get_inner_cone_angle);
@@ -46,7 +46,7 @@ void GLTFLight::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); // Color
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity"), "set_intensity", "get_intensity"); // float
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "type"), "set_type", "get_type"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "light_type"), "set_light_type", "get_light_type"); // String
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range"), "set_range", "get_range"); // float
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_cone_angle"), "set_inner_cone_angle", "get_inner_cone_angle"); // float
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float
@@ -68,12 +68,12 @@ void GLTFLight::set_intensity(float p_intensity) {
intensity = p_intensity;
}
-String GLTFLight::get_type() {
- return type;
+String GLTFLight::get_light_type() {
+ return light_type;
}
-void GLTFLight::set_type(String p_type) {
- type = p_type;
+void GLTFLight::set_light_type(String p_light_type) {
+ light_type = p_light_type;
}
float GLTFLight::get_range() {
diff --git a/modules/gltf/gltf_light.h b/modules/gltf/gltf_light.h
index a859ca1833..62a20d2f16 100644
--- a/modules/gltf/gltf_light.h
+++ b/modules/gltf/gltf_light.h
@@ -42,12 +42,12 @@ protected:
static void _bind_methods();
private:
- Color color;
- float intensity = 0.0f;
- String type;
- float range = 0.0f;
+ Color color = Color(1.0f, 1.0f, 1.0f);
+ float intensity = 1.0f;
+ String light_type;
+ float range = INFINITY;
float inner_cone_angle = 0.0f;
- float outer_cone_angle = 0.0f;
+ float outer_cone_angle = Math_TAU / 8.0f;
public:
Color get_color();
@@ -56,8 +56,8 @@ public:
float get_intensity();
void set_intensity(float p_intensity);
- String get_type();
- void set_type(String p_type);
+ String get_light_type();
+ void set_light_type(String p_light_type);
float get_range();
void set_range(float p_range);
diff --git a/modules/gltf/gltf_mesh.cpp b/modules/gltf/gltf_mesh.cpp
index 8c10e42c89..7134345b30 100644
--- a/modules/gltf/gltf_mesh.cpp
+++ b/modules/gltf/gltf_mesh.cpp
@@ -29,26 +29,37 @@
/*************************************************************************/
#include "gltf_mesh.h"
-#include "editor/import/scene_importer_mesh.h"
+#include "scene/resources/importer_mesh.h"
void GLTFMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFMesh::get_mesh);
ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &GLTFMesh::set_mesh);
ClassDB::bind_method(D_METHOD("get_blend_weights"), &GLTFMesh::get_blend_weights);
ClassDB::bind_method(D_METHOD("set_blend_weights", "blend_weights"), &GLTFMesh::set_blend_weights);
+ ClassDB::bind_method(D_METHOD("get_instance_materials"), &GLTFMesh::get_instance_materials);
+ ClassDB::bind_method(D_METHOD("set_instance_materials", "instance_materials"), &GLTFMesh::set_instance_materials);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh"), "set_mesh", "get_mesh");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT32_ARRAY, "blend_weights"), "set_blend_weights", "get_blend_weights"); // Vector<float>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "instance_materials"), "set_instance_materials", "get_instance_materials");
}
-Ref<EditorSceneImporterMesh> GLTFMesh::get_mesh() {
+Ref<ImporterMesh> GLTFMesh::get_mesh() {
return mesh;
}
-void GLTFMesh::set_mesh(Ref<EditorSceneImporterMesh> p_mesh) {
+void GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) {
mesh = p_mesh;
}
+Array GLTFMesh::get_instance_materials() {
+ return instance_materials;
+}
+
+void GLTFMesh::set_instance_materials(Array p_instance_materials) {
+ instance_materials = p_instance_materials;
+}
+
Vector<float> GLTFMesh::get_blend_weights() {
return blend_weights;
}
diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/gltf_mesh.h
index 0fc750fc9f..cc2be93c09 100644
--- a/modules/gltf/gltf_mesh.h
+++ b/modules/gltf/gltf_mesh.h
@@ -33,23 +33,27 @@
#include "core/io/resource.h"
#include "editor/import/resource_importer_scene.h"
-#include "editor/import/scene_importer_mesh.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
+#include "scene/resources/importer_mesh.h"
#include "scene/resources/mesh.h"
class GLTFMesh : public Resource {
GDCLASS(GLTFMesh, Resource);
private:
- Ref<EditorSceneImporterMesh> mesh;
+ Ref<ImporterMesh> mesh;
Vector<float> blend_weights;
+ Array instance_materials;
protected:
static void _bind_methods();
public:
- Ref<EditorSceneImporterMesh> get_mesh();
- void set_mesh(Ref<EditorSceneImporterMesh> p_mesh);
+ Ref<ImporterMesh> get_mesh();
+ void set_mesh(Ref<ImporterMesh> p_mesh);
Vector<float> get_blend_weights();
void set_blend_weights(Vector<float> p_blend_weights);
+ Array get_instance_materials();
+ void set_instance_materials(Array p_instance_materials);
};
#endif // GLTF_MESH_H
diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/gltf_node.cpp
index 5db7ad66c3..9f925c7bbc 100644
--- a/modules/gltf/gltf_node.cpp
+++ b/modules/gltf/gltf_node.cpp
@@ -47,8 +47,8 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &GLTFNode::set_skeleton);
ClassDB::bind_method(D_METHOD("get_joint"), &GLTFNode::get_joint);
ClassDB::bind_method(D_METHOD("set_joint", "joint"), &GLTFNode::set_joint);
- ClassDB::bind_method(D_METHOD("get_translation"), &GLTFNode::get_translation);
- ClassDB::bind_method(D_METHOD("set_translation", "translation"), &GLTFNode::set_translation);
+ ClassDB::bind_method(D_METHOD("get_position"), &GLTFNode::get_position);
+ ClassDB::bind_method(D_METHOD("set_position", "position"), &GLTFNode::set_position);
ClassDB::bind_method(D_METHOD("get_rotation"), &GLTFNode::get_rotation);
ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &GLTFNode::set_rotation);
ClassDB::bind_method(D_METHOD("get_scale"), &GLTFNode::get_scale);
@@ -66,7 +66,7 @@ void GLTFNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "skin"), "set_skin", "get_skin"); // GLTFSkinIndex
ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // GLTFSkeletonIndex
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "joint"), "set_joint", "get_joint"); // bool
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation"), "set_translation", "get_translation"); // Vector3
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "set_position", "get_position"); // Vector3
ADD_PROPERTY(PropertyInfo(Variant::QUATERNION, "rotation"), "set_rotation", "get_rotation"); // Quaternion
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int>
@@ -137,12 +137,12 @@ void GLTFNode::set_joint(bool p_joint) {
joint = p_joint;
}
-Vector3 GLTFNode::get_translation() {
- return translation;
+Vector3 GLTFNode::get_position() {
+ return position;
}
-void GLTFNode::set_translation(Vector3 p_translation) {
- translation = p_translation;
+void GLTFNode::set_position(Vector3 p_position) {
+ position = p_position;
}
Quaternion GLTFNode::get_rotation() {
diff --git a/modules/gltf/gltf_node.h b/modules/gltf/gltf_node.h
index 378b6da8bf..3b6e061449 100644
--- a/modules/gltf/gltf_node.h
+++ b/modules/gltf/gltf_node.h
@@ -37,7 +37,6 @@
class GLTFNode : public Resource {
GDCLASS(GLTFNode, Resource);
friend class GLTFDocument;
- friend class PackedSceneGLTF;
private:
// matrices need to be transformed to this
@@ -49,7 +48,7 @@ private:
GLTFSkinIndex skin = -1;
GLTFSkeletonIndex skeleton = -1;
bool joint = false;
- Vector3 translation;
+ Vector3 position;
Quaternion rotation;
Vector3 scale = Vector3(1, 1, 1);
Vector<int> children;
@@ -83,8 +82,8 @@ public:
bool get_joint();
void set_joint(bool p_joint);
- Vector3 get_translation();
- void set_translation(Vector3 p_translation);
+ Vector3 get_position();
+ void set_position(Vector3 p_position);
Quaternion get_rotation();
void set_rotation(Quaternion p_rotation);
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index d8209523c5..61faba0dc5 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -36,6 +36,7 @@
#include "gltf_buffer_view.h"
#include "gltf_camera.h"
#include "gltf_document.h"
+#include "gltf_document_extension.h"
#include "gltf_light.h"
#include "gltf_mesh.h"
#include "gltf_node.h"
@@ -44,6 +45,8 @@
#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 "scene/animation/animation_player.h"
#include "scene/resources/texture.h"
@@ -51,7 +54,6 @@
class GLTFState : public Resource {
GDCLASS(GLTFState, Resource);
friend class GLTFDocument;
- friend class PackedSceneGLTF;
String filename;
Dictionary json;
@@ -88,6 +90,9 @@ class GLTFState : public Resource {
Vector<Ref<GLTFAnimation>> animations;
Map<GLTFNodeIndex, Node *> scene_nodes;
+ Map<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
+ Map<ObjectID, Map<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+
protected:
static void _bind_methods();
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index 85921490d2..5a60c2d328 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -38,6 +38,8 @@
#include "gltf_buffer_view.h"
#include "gltf_camera.h"
#include "gltf_document.h"
+#include "gltf_document_extension.h"
+#include "gltf_document_extension_convert_importer_mesh.h"
#include "gltf_light.h"
#include "gltf_mesh.h"
#include "gltf_node.h"
@@ -50,7 +52,7 @@
#ifndef _3D_DISABLED
#ifdef TOOLS_ENABLED
static void _editor_init() {
- Ref<EditorSceneImporterGLTF> import_gltf;
+ Ref<EditorSceneFormatImporterGLTF> import_gltf;
import_gltf.instantiate();
ResourceImporterScene::get_singleton()->add_importer(import_gltf);
}
@@ -62,7 +64,7 @@ void register_gltf_types() {
#ifdef TOOLS_ENABLED
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);
- GDREGISTER_CLASS(EditorSceneImporterGLTF);
+ GDREGISTER_CLASS(EditorSceneFormatImporterGLTF);
GDREGISTER_CLASS(GLTFMesh);
EditorPlugins::add_by_type<SceneExporterGLTFPlugin>();
ClassDB::set_current_api(prev_api);
@@ -79,8 +81,9 @@ void register_gltf_types() {
GDREGISTER_CLASS(GLTFCamera);
GDREGISTER_CLASS(GLTFLight);
GDREGISTER_CLASS(GLTFState);
+ GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh);
+ GDREGISTER_CLASS(GLTFDocumentExtension);
GDREGISTER_CLASS(GLTFDocument);
- GDREGISTER_CLASS(PackedSceneGLTF);
#endif
}
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index c1dbe63628..73315350ff 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -11,7 +11,7 @@
[b]Note:[/b] GridMap doesn't extend [VisualInstance3D] and therefore can't be hidden or cull masked based on [member VisualInstance3D.layers]. If you make a light not affect the first layer, the whole GridMap won't be lit by the light in question.
</description>
<tutorials>
- <link title="Using gridmaps">https://docs.godotengine.org/en/latest/tutorials/3d/using_gridmaps.html</link>
+ <link title="Using gridmaps">$DOCS_URL/tutorials/3d/using_gridmaps.html</link>
<link title="3D Platformer Demo">https://godotengine.org/asset-library/asset/125</link>
<link title="3D Kinematic Character Demo">https://godotengine.org/asset-library/asset/126</link>
</tutorials>
@@ -173,7 +173,7 @@
GridMaps act as static bodies, meaning they aren't affected by gravity or other forces. They only affect other physics bodies that collide with them.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The physics layers this GridMap detects collisions in. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
+ The physics layers this GridMap detects collisions in. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="mesh_library" type="MeshLibrary" setter="set_mesh_library" getter="get_mesh_library">
The assigned [MeshLibrary].
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 8e8b6f14ad..c9d8f2b42b 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -377,10 +377,7 @@ int GridMap::get_cell_item_orientation(const Vector3i &p_position) const {
}
Vector3i GridMap::world_to_map(const Vector3 &p_world_position) const {
- Vector3 map_position = p_world_position / cell_size;
- map_position.x = floor(map_position.x);
- map_position.y = floor(map_position.y);
- map_position.z = floor(map_position.z);
+ Vector3 map_position = (p_world_position / cell_size).floor();
return Vector3i(map_position);
}
@@ -423,8 +420,8 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
//erase navigation
- for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
- NavigationServer3D::get_singleton()->free(E->get().region);
+ for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
}
g.navmesh_ids.clear();
@@ -475,7 +472,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
Pair<Transform3D, IndexKey> p;
- p.first = xform;
+ p.first = xform * mesh_library->get_item_mesh_transform(c.item);
p.second = E->get();
multimesh_items[c.item].push_back(p);
}
@@ -515,15 +512,15 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
//update multimeshes, only if not baked
if (baked_meshes.size() == 0) {
- for (Map<int, List<Pair<Transform3D, IndexKey>>>::Element *E = multimesh_items.front(); E; E = E->next()) {
+ for (const KeyValue<int, List<Pair<Transform3D, IndexKey>>> &E : multimesh_items) {
Octant::MultimeshInstance mmi;
RID mm = RS::get_singleton()->multimesh_create();
- RS::get_singleton()->multimesh_allocate_data(mm, E->get().size(), RS::MULTIMESH_TRANSFORM_3D);
- RS::get_singleton()->multimesh_set_mesh(mm, mesh_library->get_item_mesh(E->key())->get_rid());
+ RS::get_singleton()->multimesh_allocate_data(mm, E.value.size(), RS::MULTIMESH_TRANSFORM_3D);
+ RS::get_singleton()->multimesh_set_mesh(mm, mesh_library->get_item_mesh(E.key)->get_rid());
int idx = 0;
- for (const Pair<Transform3D, IndexKey> &F : E->get()) {
+ for (const Pair<Transform3D, IndexKey> &F : E.value) {
RS::get_singleton()->multimesh_instance_set_transform(mm, idx, F.first);
#ifdef TOOLS_ENABLED
@@ -570,9 +567,9 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
}
void GridMap::_reset_physic_bodies_collision_filters() {
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- PhysicsServer3D::get_singleton()->body_set_collision_layer(E->get()->static_body, collision_layer);
- PhysicsServer3D::get_singleton()->body_set_collision_mask(E->get()->static_body, collision_mask);
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ PhysicsServer3D::get_singleton()->body_set_collision_layer(E.value->static_body, collision_layer);
+ PhysicsServer3D::get_singleton()->body_set_collision_mask(E.value->static_body, collision_mask);
}
}
@@ -593,17 +590,17 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
}
if (bake_navigation && mesh_library.is_valid()) {
- for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
- if (cell_map.has(F->key()) && F->get().region.is_valid() == false) {
- Ref<NavigationMesh> nm = mesh_library->get_item_navmesh(cell_map[F->key()].item);
+ for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) {
+ if (cell_map.has(F.key) && F.value.region.is_valid() == false) {
+ 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_navmesh(region, nm);
- NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * F->get().xform);
+ 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());
- F->get().region = region;
+ F.value.region = region;
}
}
}
@@ -624,10 +621,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
RS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID());
}
- for (Map<IndexKey, Octant::NavMesh>::Element *F = g.navmesh_ids.front(); F; F = F->next()) {
- if (F->get().region.is_valid()) {
- NavigationServer3D::get_singleton()->free(F->get().region);
- F->get().region = RID();
+ for (KeyValue<IndexKey, Octant::NavMesh> &F : g.navmesh_ids) {
+ if (F.value.region.is_valid()) {
+ NavigationServer3D::get_singleton()->free(F.value.region);
+ F.value.region = RID();
}
}
}
@@ -646,8 +643,8 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
PhysicsServer3D::get_singleton()->free(g.static_body);
// Erase navigation
- for (Map<IndexKey, Octant::NavMesh>::Element *E = g.navmesh_ids.front(); E; E = E->next()) {
- NavigationServer3D::get_singleton()->free(E->get().region);
+ for (const KeyValue<IndexKey, Octant::NavMesh> &E : g.navmesh_ids) {
+ NavigationServer3D::get_singleton()->free(E.value.region);
}
g.navmesh_ids.clear();
@@ -665,8 +662,8 @@ void GridMap::_notification(int p_what) {
case NOTIFICATION_ENTER_WORLD: {
last_transform = get_global_transform();
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- _octant_enter_world(E->key());
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ _octant_enter_world(E.key);
}
for (int i = 0; i < baked_meshes.size(); i++) {
@@ -681,8 +678,8 @@ void GridMap::_notification(int p_what) {
break;
}
//update run
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- _octant_transform(E->key());
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ _octant_transform(E.key);
}
last_transform = new_xform;
@@ -692,8 +689,8 @@ void GridMap::_notification(int p_what) {
}
} break;
case NOTIFICATION_EXIT_WORLD: {
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- _octant_exit_world(E->key());
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ _octant_exit_world(E.key);
}
//_queue_octants_dirty(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS);
@@ -715,8 +712,8 @@ void GridMap::_update_visibility() {
return;
}
- for (Map<OctantKey, Octant *>::Element *e = octant_map.front(); e; e = e->next()) {
- Octant *octant = e->value();
+ for (KeyValue<OctantKey, Octant *> &e : octant_map) {
+ Octant *octant = e.value;
for (int i = 0; i < octant->multimesh_instances.size(); i++) {
const Octant::MultimeshInstance &mi = octant->multimesh_instances[i];
RS::get_singleton()->instance_set_visible(mi.instance, is_visible_in_tree());
@@ -741,20 +738,20 @@ void GridMap::_recreate_octant_data() {
recreating_octants = true;
Map<IndexKey, Cell> cell_copy = cell_map;
_clear_internal();
- for (Map<IndexKey, Cell>::Element *E = cell_copy.front(); E; E = E->next()) {
- set_cell_item(Vector3i(E->key()), E->get().item, E->get().rot);
+ for (const KeyValue<IndexKey, Cell> &E : cell_copy) {
+ set_cell_item(Vector3i(E.key), E.value.item, E.value.rot);
}
recreating_octants = false;
}
void GridMap::_clear_internal() {
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
if (is_inside_world()) {
- _octant_exit_world(E->key());
+ _octant_exit_world(E.key);
}
- _octant_clean_up(E->key());
- memdelete(E->get());
+ _octant_clean_up(E.key);
+ memdelete(E.value);
}
octant_map.clear();
@@ -776,9 +773,9 @@ void GridMap::_update_octants_callback() {
}
List<OctantKey> to_delete;
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- if (_octant_update(E->key())) {
- to_delete.push_back(E->key());
+ for (const KeyValue<OctantKey, Octant *> &E : octant_map) {
+ if (_octant_update(E.key)) {
+ to_delete.push_back(E.key);
}
}
@@ -886,8 +883,8 @@ void GridMap::set_clip(bool p_enabled, bool p_clip_above, int p_floor, Vector3::
clip_above = p_clip_above;
//make it all update
- for (Map<OctantKey, Octant *>::Element *E = octant_map.front(); E; E = E->next()) {
- Octant *g = E->get();
+ for (KeyValue<OctantKey, Octant *> &E : octant_map) {
+ Octant *g = E.value;
g->dirty = true;
}
awaiting_update = true;
@@ -907,8 +904,8 @@ Array GridMap::get_used_cells() const {
Array a;
a.resize(cell_map.size());
int i = 0;
- for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) {
- Vector3 p(E->key().x, E->key().y, E->key().z);
+ for (const KeyValue<IndexKey, Cell> &E : cell_map) {
+ Vector3 p(E.key.x, E.key.y, E.key.z);
a[i++] = p;
}
@@ -923,8 +920,8 @@ Array GridMap::get_meshes() {
Vector3 ofs = _get_offset();
Array meshes;
- for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) {
- int id = E->get().item;
+ for (KeyValue<IndexKey, Cell> &E : cell_map) {
+ int id = E.value.item;
if (!mesh_library->has_item(id)) {
continue;
}
@@ -933,13 +930,13 @@ Array GridMap::get_meshes() {
continue;
}
- IndexKey ik = E->key();
+ IndexKey ik = E.key;
Vector3 cellpos = Vector3(ik.x, ik.y, ik.z);
Transform3D xform;
- xform.basis.set_orthogonal_index(E->get().rot);
+ xform.basis.set_orthogonal_index(E.value.rot);
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
@@ -975,10 +972,10 @@ 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;
- for (Map<IndexKey, Cell>::Element *E = cell_map.front(); E; E = E->next()) {
- IndexKey key = E->key();
+ for (KeyValue<IndexKey, Cell> &E : cell_map) {
+ IndexKey key = E.key;
- int item = E->get().item;
+ int item = E.value.item;
if (!mesh_library->has_item(item)) {
continue;
}
@@ -993,7 +990,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
Transform3D xform;
- xform.basis.set_orthogonal_index(E->get().rot);
+ xform.basis.set_orthogonal_index(E.value.rot);
xform.set_origin(cellpos * cell_size + ofs);
xform.basis.scale(Vector3(cell_scale, cell_scale, cell_scale));
@@ -1026,11 +1023,11 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
}
}
- for (Map<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>>::Element *E = surface_map.front(); E; E = E->next()) {
+ for (KeyValue<OctantKey, Map<Ref<Material>, Ref<SurfaceTool>>> &E : surface_map) {
Ref<ArrayMesh> mesh;
mesh.instantiate();
- for (Map<Ref<Material>, Ref<SurfaceTool>>::Element *F = E->get().front(); F; F = F->next()) {
- F->get()->commit(mesh);
+ for (KeyValue<Ref<Material>, Ref<SurfaceTool>> &F : E.value) {
+ F.value->commit(mesh);
}
BakedMesh bm;
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 2331a12d0f..c2169c6335 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -255,6 +255,12 @@ void GridMapEditor::_update_cursor_transform() {
cursor_transform.basis *= node->get_cell_scale();
cursor_transform = node->get_global_transform() * cursor_transform;
+ if (selected_palette >= 0) {
+ if (node && !node->get_mesh_library().is_null()) {
+ cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette);
+ }
+ }
+
if (cursor_instance.is_valid()) {
RenderingServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform);
RenderingServer::get_singleton()->instance_set_visible(cursor_instance, cursor_visible);
@@ -597,32 +603,32 @@ void GridMapEditor::_do_paste() {
_clear_clipboard_data();
}
-bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && (mb->is_command_pressed() || mb->is_shift_pressed())) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_pressed() || mb->is_shift_pressed())) {
if (mb->is_pressed()) {
floor->set_value(floor->get_value() + mb->get_factor());
}
- return true; // Eaten.
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP; // Eaten.
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && (mb->is_command_pressed() || mb->is_shift_pressed())) {
if (mb->is_pressed()) {
floor->set_value(floor->get_value() - mb->get_factor());
}
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
if (mb->is_pressed()) {
Node3DEditorViewport::NavigationScheme nav_scheme = (Node3DEditorViewport::NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
if ((nav_scheme == Node3DEditorViewport::NAVIGATION_MAYA || nav_scheme == Node3DEditorViewport::NAVIGATION_MODO) && mb->is_alt_pressed()) {
input_action = INPUT_NONE;
- } else if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ } else if (mb->get_button_index() == MouseButton::LEFT) {
bool can_edit = (node && node->get_mesh_library().is_valid());
if (input_action == INPUT_PASTE) {
_do_paste();
@@ -637,26 +643,29 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
input_action = INPUT_PAINT;
set_items.clear();
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT) {
if (input_action == INPUT_PASTE) {
_clear_clipboard_data();
input_action = INPUT_NONE;
_update_paste_indicator();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (selection.active) {
_set_selection(false);
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
input_action = INPUT_ERASE;
set_items.clear();
}
} else {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- return do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true);
+ if (do_input_action(p_camera, Point2(mb->get_position().x, mb->get_position().y), true)) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
} else {
- if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_PAINT)) {
+ if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
undo_redo->create_action(TTR("GridMap Paint"));
for (const SetItem &si : set_items) {
@@ -671,24 +680,28 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
}
set_items.clear();
input_action = INPUT_NONE;
- return set_items.size() > 0;
+
+ if (set_items.size() > 0) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action == INPUT_SELECT) {
+ if (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_SELECT) {
undo_redo->create_action(TTR("GridMap Selection"));
undo_redo->add_do_method(this, "_set_selection", selection.active, selection.begin, selection.end);
undo_redo->add_undo_method(this, "_set_selection", last_selection.active, last_selection.begin, last_selection.end);
undo_redo->commit_action();
}
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT && input_action != INPUT_NONE) {
+ if (mb->get_button_index() == MouseButton::LEFT && input_action != INPUT_NONE) {
set_items.clear();
input_action = INPUT_NONE;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) {
+ if (mb->get_button_index() == MouseButton::RIGHT && (input_action == INPUT_ERASE || input_action == INPUT_PASTE)) {
input_action = INPUT_NONE;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -696,41 +709,44 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- return do_input_action(p_camera, mm->get_position(), false);
+ if (do_input_action(p_camera, mm->get_position(), false)) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<InputEventKey> k = p_event;
if (k.is_valid()) {
if (k->is_pressed()) {
- if (k->get_keycode() == KEY_ESCAPE) {
+ if (k->get_keycode() == Key::ESCAPE) {
if (input_action == INPUT_PASTE) {
_clear_clipboard_data();
input_action = INPUT_NONE;
_update_paste_indicator();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (selection.active) {
_set_selection(false);
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
selected_palette = -1;
mesh_library_palette->deselect_all();
update_palette();
_update_cursor_instance();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
if (k->is_shift_pressed() && selection.active && input_action != INPUT_PASTE) {
- if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
+ if (k->get_keycode() == (Key)options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL))) {
selection.click[edit_axis]--;
_validate_selection();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
- if (k->get_keycode() == options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
+ if (k->get_keycode() == (Key)options->get_popup()->get_item_accelerator(options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL))) {
selection.click[edit_axis]++;
_validate_selection();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
@@ -743,18 +759,18 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
accumulated_floor_delta += delta;
int step = 0;
if (ABS(accumulated_floor_delta) > 1.0) {
- step = SGN(accumulated_floor_delta);
+ step = SIGN(accumulated_floor_delta);
accumulated_floor_delta -= step;
}
if (step) {
floor->set_value(floor->get_value() + step);
}
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
accumulated_floor_delta = 0.0;
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
struct _CGMEItemSort {
@@ -788,7 +804,7 @@ void GridMapEditor::_text_changed(const String &p_text) {
void GridMapEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
const Ref<InputEventKey> k = p_ie;
- if (k.is_valid() && (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_PAGEUP || k->get_keycode() == KEY_PAGEDOWN)) {
+ if (k.is_valid() && (k->get_keycode() == Key::UP || k->get_keycode() == Key::DOWN || k->get_keycode() == Key::PAGEUP || k->get_keycode() == Key::PAGEDOWN)) {
// Forward the key input to the ItemList so it can be scrolled
mesh_library_palette->gui_input(k);
search_box->accept_event();
@@ -800,11 +816,11 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) {
// Zoom in/out using Ctrl + mouse wheel
if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed()) {
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
size_slider->set_value(size_slider->get_value() + 0.2);
}
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
size_slider->set_value(size_slider->get_value() - 0.2);
}
}
@@ -869,11 +885,11 @@ void GridMapEditor::update_palette() {
String name = mesh_library->get_item_name(id);
Ref<Texture2D> preview = mesh_library->get_item_preview(id);
- if (name == "") {
+ if (name.is_empty()) {
name = "#" + itos(id);
}
- if (filter != "" && !filter.is_subsequence_ofi(name)) {
+ if (!filter.is_empty() && !filter.is_subsequence_ofi(name)) {
continue;
}
@@ -1081,7 +1097,7 @@ void GridMapEditor::_notification(int p_what) {
// Simulate mouse released event to stop drawing when editor focus exists.
Ref<InputEventMouseButton> release;
release.instantiate();
- release->set_button_index(MOUSE_BUTTON_LEFT);
+ release->set_button_index(MouseButton::LEFT);
forward_spatial_input_event(nullptr, release);
}
} break;
@@ -1147,7 +1163,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
spatial_editor_hb = memnew(HBoxContainer);
spatial_editor_hb->set_h_size_flags(SIZE_EXPAND_FILL);
- spatial_editor_hb->set_alignment(BoxContainer::ALIGN_END);
+ spatial_editor_hb->set_alignment(BoxContainer::ALIGNMENT_END);
Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
spin_box_label = memnew(Label);
@@ -1172,33 +1188,33 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
spatial_editor_hb->hide();
options->set_text(TTR("Grid Map"));
- options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, KEY_Q);
- options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, KEY_E);
+ options->get_popup()->add_item(TTR("Previous Floor"), MENU_OPTION_PREV_LEVEL, Key::Q);
+ options->get_popup()->add_item(TTR("Next Floor"), MENU_OPTION_NEXT_LEVEL, Key::E);
options->get_popup()->add_separator();
options->get_popup()->add_radio_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_CLIP_DISABLED), true);
options->get_popup()->add_radio_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
options->get_popup()->add_radio_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
options->get_popup()->add_separator();
- options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
- options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
- options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, KEY_C);
+ options->get_popup()->add_radio_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, Key::Z);
+ options->get_popup()->add_radio_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, Key::X);
+ options->get_popup()->add_radio_check_item(TTR("Edit Z Axis"), MENU_OPTION_Z_AXIS, Key::C);
options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, KEY_A);
- options->get_popup()->add_item(TTR("Cursor Rotate Y"), MENU_OPTION_CURSOR_ROTATE_Y, KEY_S);
- options->get_popup()->add_item(TTR("Cursor Rotate Z"), MENU_OPTION_CURSOR_ROTATE_Z, KEY_D);
- options->get_popup()->add_item(TTR("Cursor Back Rotate X"), MENU_OPTION_CURSOR_BACK_ROTATE_X, KEY_MASK_SHIFT + KEY_A);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y, KEY_MASK_SHIFT + KEY_S);
- options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KEY_MASK_SHIFT + KEY_D);
- options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, KEY_W);
+ options->get_popup()->add_item(TTR("Cursor Rotate X"), MENU_OPTION_CURSOR_ROTATE_X, Key::A);
+ options->get_popup()->add_item(TTR("Cursor Rotate Y"), MENU_OPTION_CURSOR_ROTATE_Y, Key::S);
+ options->get_popup()->add_item(TTR("Cursor Rotate Z"), MENU_OPTION_CURSOR_ROTATE_Z, Key::D);
+ options->get_popup()->add_item(TTR("Cursor Back Rotate X"), MENU_OPTION_CURSOR_BACK_ROTATE_X, KeyModifierMask::SHIFT + Key::A);
+ options->get_popup()->add_item(TTR("Cursor Back Rotate Y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y, KeyModifierMask::SHIFT + Key::S);
+ options->get_popup()->add_item(TTR("Cursor Back Rotate Z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z, KeyModifierMask::SHIFT + Key::D);
+ options->get_popup()->add_item(TTR("Cursor Clear Rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION, Key::W);
options->get_popup()->add_separator();
options->get_popup()->add_check_item(TTR("Paste Selects"), MENU_OPTION_PASTE_SELECTS);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KEY_MASK_CTRL + KEY_C);
- options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KEY_MASK_CTRL + KEY_X);
- options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, KEY_DELETE);
- options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KEY_MASK_CTRL + KEY_F);
+ options->get_popup()->add_item(TTR("Duplicate Selection"), MENU_OPTION_SELECTION_DUPLICATE, KeyModifierMask::CTRL + Key::C);
+ options->get_popup()->add_item(TTR("Cut Selection"), MENU_OPTION_SELECTION_CUT, KeyModifierMask::CTRL + Key::X);
+ options->get_popup()->add_item(TTR("Clear Selection"), MENU_OPTION_SELECTION_CLEAR, Key::KEY_DELETE);
+ options->get_popup()->add_item(TTR("Fill Selection"), MENU_OPTION_SELECTION_FILL, KeyModifierMask::CTRL + Key::F);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
@@ -1264,8 +1280,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
info_message = memnew(Label);
info_message->set_text(TTR("Give a MeshLibrary resource to this GridMap to use its meshes."));
- info_message->set_valign(Label::VALIGN_CENTER);
- info_message->set_align(Label::ALIGN_CENTER);
+ 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_custom_minimum_size(Size2(100 * EDSCALE, 0));
info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 4bc849e071..1a6b1310d8 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -232,7 +232,7 @@ protected:
static void _bind_methods();
public:
- bool forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ EditorPlugin::AfterGUIInput forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(GridMap *p_gridmap);
GridMapEditor() {}
@@ -250,7 +250,7 @@ protected:
void _notification(int p_what);
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
virtual String get_name() const override { return "GridMap"; }
bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index 9d6a399eff..ea28d0c0c8 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -41,7 +41,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
while (true) {
String line = f->get_line();
ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_UNRECOGNIZED);
- if (line == "") { // empty line indicates end of header
+ if (line.is_empty()) { // empty line indicates end of header
break;
}
if (line.begins_with("FORMAT=")) { // leave option to implement other commands
@@ -65,7 +65,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
Vector<uint8_t> imgdata;
- imgdata.resize(height * width * sizeof(uint32_t));
+ imgdata.resize(height * width * (int)sizeof(uint32_t));
{
uint8_t *w = imgdata.ptrw();
@@ -75,7 +75,7 @@ Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
if (width < 8 || width >= 32768) {
// Read flat data
- f->get_buffer(ptr, width * height * 4);
+ f->get_buffer(ptr, (uint64_t)width * height * 4);
} else {
// Read RLE-encoded data
diff --git a/modules/lightmapper_rd/SCsub b/modules/lightmapper_rd/SCsub
index 2f04f1833e..5cc9d8ee8b 100644
--- a/modules/lightmapper_rd/SCsub
+++ b/modules/lightmapper_rd/SCsub
@@ -7,6 +7,9 @@ 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")
# 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 fe941e25e7..37e969db4d 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -274,13 +274,12 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
return BAKE_OK;
}
-void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
+void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) {
HashMap<Vertex, uint32_t, VertexHash> vertex_map;
//fill triangles array and vertex array
LocalVector<Triangle> triangles;
LocalVector<Vertex> vertex_array;
- LocalVector<Box> box_array;
LocalVector<Seam> seams;
slice_triangle_count.resize(atlas_slices);
@@ -387,16 +386,13 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
}
}
- Box box;
- box.min_bounds[0] = taabb.position.x;
- box.min_bounds[1] = taabb.position.y;
- box.min_bounds[2] = taabb.position.z;
- box.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
- box.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
- box.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
- box.pad0 = box.pad1 = 0; //make valgrind not complain
- box_array.push_back(box);
-
+ t.min_bounds[0] = taabb.position.x;
+ t.min_bounds[1] = taabb.position.y;
+ t.min_bounds[2] = taabb.position.z;
+ t.max_bounds[0] = taabb.position.x + MAX(taabb.size.x, 0.0001);
+ t.max_bounds[1] = taabb.position.y + MAX(taabb.size.y, 0.0001);
+ t.max_bounds[2] = taabb.position.z + MAX(taabb.size.z, 0.0001);
+ t.pad0 = t.pad1 = 0; //make valgrind not complain
triangles.push_back(t);
slice_triangle_count.write[t.slice]++;
}
@@ -505,9 +501,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
Vector<uint8_t> tb = triangles.to_byte_array();
triangle_buffer = rd->storage_buffer_create(tb.size(), tb);
- Vector<uint8_t> bb = box_array.to_byte_array();
- box_buffer = rd->storage_buffer_create(bb.size(), bb);
-
Vector<uint8_t> tib = triangle_indices.to_byte_array();
triangle_cell_indices_buffer = rd->storage_buffer_create(tib.size(), tib);
@@ -618,6 +611,61 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int
}
}
+LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices) {
+ Vector<RD::Uniform> uniforms;
+ {
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 0;
+ u.ids.push_back(dest_light_tex);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 1;
+ u.ids.push_back(source_light_tex);
+ uniforms.push_back(u);
+ }
+ }
+
+ RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate"));
+ ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
+ RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate);
+
+ RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_dilate, 1);
+
+ RD::ComputeListID compute_list = rd->compute_list_begin();
+ rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline);
+ rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
+ rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1);
+ push_constant.region_ofs[0] = 0;
+ push_constant.region_ofs[1] = 0;
+ Vector3i group_size((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); //restore group size
+
+ for (int i = 0; i < atlas_slices; i++) {
+ push_constant.atlas_slice = i;
+ rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
+ rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
+ //no barrier, let them run all together
+ }
+ rd->compute_list_end();
+ rd->free(compute_shader_dilate);
+
+#ifdef DEBUG_TEXTURES
+ for (int i = 0; i < atlas_slices; i++) {
+ Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
+ Ref<Image> img;
+ img.instantiate();
+ img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
+ img->convert(Image::FORMAT_RGBA8);
+ img->save_png("res://5_dilated_" + itos(i) + ".png");
+ }
+#endif
+ return BAKE_OK;
+}
+
LightmapperRD::BakeError LightmapperRD::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, void *p_bake_userdata) {
if (p_step_function) {
p_step_function(0.0, TTR("Begin Bake"), p_bake_userdata, true);
@@ -755,7 +803,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Vector<int> slice_triangle_count;
RID vertex_buffer;
RID triangle_buffer;
- RID box_buffer;
RID lights_buffer;
RID triangle_cell_indices_buffer;
RID grid_texture;
@@ -767,14 +814,13 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
#define FREE_BUFFERS \
rd->free(vertex_buffer); \
rd->free(triangle_buffer); \
- rd->free(box_buffer); \
rd->free(lights_buffer); \
rd->free(triangle_cell_indices_buffer); \
rd->free(grid_texture); \
rd->free(seams_buffer); \
rd->free(probe_positions_buffer);
- _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, box_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
+ _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata);
if (p_step_function) {
p_step_function(0.47, TTR("Preparing shaders"), p_bake_userdata, true);
@@ -828,62 +874,55 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 3;
- u.ids.push_back(box_buffer);
- base_uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 4;
u.ids.push_back(triangle_cell_indices_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 5;
+ u.binding = 4;
u.ids.push_back(lights_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 6;
+ u.binding = 5;
u.ids.push_back(seams_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 7;
+ u.binding = 6;
u.ids.push_back(probe_positions_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 8;
+ u.binding = 7;
u.ids.push_back(grid_texture);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 9;
+ u.binding = 8;
u.ids.push_back(albedo_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 10;
+ u.binding = 9;
u.ids.push_back(emission_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 11;
+ u.binding = 10;
u.ids.push_back(sampler);
base_uniforms.push_back(u);
}
@@ -959,11 +998,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
ERR_FAIL_COND_V(compute_shader_secondary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
RID compute_shader_secondary_pipeline = rd->compute_pipeline_create(compute_shader_secondary);
- // Dilate
- RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate"));
- ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
- RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate);
-
// Light probes
RID compute_shader_light_probes = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("light_probes"));
ERR_FAIL_COND_V(compute_shader_light_probes.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
@@ -975,7 +1009,6 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
rd->free(compute_shader_unocclude); \
rd->free(compute_shader_primary); \
rd->free(compute_shader_secondary); \
- rd->free(compute_shader_dilate); \
rd->free(compute_shader_light_probes);
PushConstant push_constant;
@@ -1286,7 +1319,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
push_constant.environment_xform[3] = 0.0f;
}
- /* LIGHPROBES */
+ /* LIGHTPROBES */
RID light_probe_buffer;
@@ -1393,6 +1426,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
#endif
+ {
+ SWAP(light_accum_tex, light_accum_tex2);
+ BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1));
+ if (unlikely(error != BAKE_OK)) {
+ return error;
+ }
+ }
+
/* DENOISE */
if (p_use_denoiser) {
@@ -1425,58 +1466,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
}
}
- }
-#ifdef DEBUG_TEXTURES
-
- for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
- Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
- Ref<Image> img;
- img.instantiate();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
- img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false);
- }
-#endif
-
- /* DILATE LIGHTMAP */
- {
- SWAP(light_accum_tex, light_accum_tex2);
-
- Vector<RD::Uniform> uniforms;
{
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 0;
- u.ids.push_back(light_accum_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 1;
- u.ids.push_back(light_accum_tex2);
- uniforms.push_back(u);
+ SWAP(light_accum_tex, light_accum_tex2);
+ BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1));
+ if (unlikely(error != BAKE_OK)) {
+ return error;
}
}
-
- RID dilate_uniform_set = rd->uniform_set_create(uniforms, compute_shader_dilate, 1);
-
- RD::ComputeListID compute_list = rd->compute_list_begin();
- rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_dilate_pipeline);
- rd->compute_list_bind_uniform_set(compute_list, compute_base_uniform_set, 0);
- rd->compute_list_bind_uniform_set(compute_list, dilate_uniform_set, 1);
- push_constant.region_ofs[0] = 0;
- push_constant.region_ofs[1] = 0;
- group_size = Vector3i((atlas_size.x - 1) / 8 + 1, (atlas_size.y - 1) / 8 + 1, 1); //restore group size
-
- for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
- push_constant.atlas_slice = i;
- rd->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
- rd->compute_list_dispatch(compute_list, group_size.x, group_size.y, group_size.z);
- //no barrier, let them run all together
- }
- rd->compute_list_end();
}
#ifdef DEBUG_TEXTURES
@@ -1486,8 +1483,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
Ref<Image> img;
img.instantiate();
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://5_dilated_" + itos(i) + ".png");
+ img->save_exr("res://4_light_secondary_" + itos(i) + ".exr", false);
}
#endif
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index 7ab7f34464..51ab60fc29 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -36,6 +36,7 @@
#include "scene/resources/mesh.h"
#include "servers/rendering/rendering_device.h"
+class RDShaderFile;
class LightmapperRD : public Lightmapper {
GDCLASS(LightmapperRD, Lightmapper)
@@ -72,13 +73,13 @@ class LightmapperRD : public Lightmapper {
bool operator==(const Vertex &p_vtx) const {
return (position[0] == p_vtx.position[0]) &&
- (position[1] == p_vtx.position[1]) &&
- (position[2] == p_vtx.position[2]) &&
- (uv[0] == p_vtx.uv[0]) &&
- (uv[1] == p_vtx.uv[1]) &&
- (normal_xy[0] == p_vtx.normal_xy[0]) &&
- (normal_xy[1] == p_vtx.normal_xy[1]) &&
- (normal_z == p_vtx.normal_z);
+ (position[1] == p_vtx.position[1]) &&
+ (position[2] == p_vtx.position[2]) &&
+ (uv[0] == p_vtx.uv[0]) &&
+ (uv[1] == p_vtx.uv[1]) &&
+ (normal_xy[0] == p_vtx.normal_xy[0]) &&
+ (normal_xy[1] == p_vtx.normal_xy[1]) &&
+ (normal_z == p_vtx.normal_z);
}
};
@@ -157,16 +158,13 @@ class LightmapperRD : public Lightmapper {
}
};
- struct Box {
+ struct Triangle {
+ uint32_t indices[3] = {};
+ uint32_t slice = 0;
float min_bounds[3] = {};
float pad0 = 0.0;
float max_bounds[3] = {};
float pad1 = 0.0;
- };
-
- struct Triangle {
- uint32_t indices[3] = {};
- uint32_t slice = 0;
bool operator<(const Triangle &p_triangle) const {
return slice < p_triangle.slice;
}
@@ -231,9 +229,11 @@ class LightmapperRD : public Lightmapper {
Vector<Color> probe_values;
BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
- void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &box_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
+ void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
+ BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices);
+
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;
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 1581639036..58523dc1f8 100644
--- a/modules/lightmapper_rd/lm_common_inc.glsl
+++ b/modules/lightmapper_rd/lm_common_inc.glsl
@@ -16,26 +16,18 @@ vertices;
struct Triangle {
uvec3 indices;
uint slice;
-};
-
-layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles {
- Triangle data[];
-}
-triangles;
-
-struct Box {
vec3 min_bounds;
uint pad0;
vec3 max_bounds;
uint pad1;
};
-layout(set = 0, binding = 3, std430) restrict readonly buffer Boxes {
- Box data[];
+layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles {
+ Triangle data[];
}
-boxes;
+triangles;
-layout(set = 0, binding = 4, std430) restrict readonly buffer GridIndices {
+layout(set = 0, binding = 3, std430) restrict readonly buffer GridIndices {
uint data[];
}
grid_indices;
@@ -63,7 +55,7 @@ struct Light {
uint pad[3];
};
-layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
+layout(set = 0, binding = 4, std430) restrict readonly buffer Lights {
Light data[];
}
lights;
@@ -73,19 +65,23 @@ struct Seam {
uvec2 b;
};
-layout(set = 0, binding = 6, std430) restrict readonly buffer Seams {
+layout(set = 0, binding = 5, std430) restrict readonly buffer Seams {
Seam data[];
}
seams;
-layout(set = 0, binding = 7, std430) restrict readonly buffer Probes {
+layout(set = 0, binding = 6, std430) restrict readonly buffer Probes {
vec4 data[];
}
probe_positions;
-layout(set = 0, binding = 8) uniform utexture3D grid;
+layout(set = 0, binding = 7) uniform utexture3D grid;
+
+layout(set = 0, binding = 8) uniform texture2DArray albedo_tex;
+layout(set = 0, binding = 9) uniform texture2DArray emission_tex;
-layout(set = 0, binding = 9) uniform texture2DArray albedo_tex;
-layout(set = 0, binding = 10) uniform texture2DArray emission_tex;
+layout(set = 0, binding = 10) uniform sampler linear_sampler;
-layout(set = 0, binding = 11) uniform sampler linear_sampler;
+// Fragment action constants
+const uint FA_NONE = 0;
+const uint FA_SMOOTHEN_POSITION = 1;
diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl
index 9ca40535f9..7bb8346c47 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -94,13 +94,14 @@ params;
//check it, but also return distance and barycentric coords (for uv lookup)
bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, vec3 p2, out float r_distance, out vec3 r_barycentric) {
+ const float EPSILON = 0.00001;
const vec3 e0 = p1 - p0;
const vec3 e1 = p0 - p2;
vec3 triangle_normal = cross(e1, e0);
float n_dot_dir = dot(triangle_normal, dir);
- if (abs(n_dot_dir) < 0.01) {
+ if (abs(n_dot_dir) < EPSILON) {
return false;
}
@@ -115,7 +116,12 @@ bool ray_hits_triangle(vec3 from, vec3 dir, float max_dist, vec3 p0, vec3 p1, ve
return (r_distance > params.bias) && (r_distance < max_dist) && all(greaterThanEqual(r_barycentric, vec3(0.0)));
}
-bool trace_ray(vec3 p_from, vec3 p_to
+const uint RAY_MISS = 0;
+const uint RAY_FRONT = 1;
+const uint RAY_BACK = 2;
+const uint RAY_ANY = 3;
+
+uint trace_ray(vec3 p_from, vec3 p_to
#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
,
out uint r_triangle, out vec3 r_barycentric
@@ -125,6 +131,7 @@ bool trace_ray(vec3 p_from, vec3 p_to
out float r_distance, out vec3 r_normal
#endif
) {
+
/* world coords */
vec3 rel = p_to - p_from;
@@ -142,7 +149,7 @@ bool trace_ray(vec3 p_from, vec3 p_to
ivec3 icell = ivec3(from_cell);
ivec3 iendcell = ivec3(to_cell);
vec3 dir_cell = normalize(rel_cell);
- vec3 delta = abs(1.0 / dir_cell); //vec3(length(rel_cell)) / rel_cell);
+ vec3 delta = min(abs(1.0 / dir_cell), params.grid_size); // use params.grid_size as max to prevent infinity values
ivec3 step = ivec3(sign(rel_cell));
vec3 side = (sign(rel_cell) * (vec3(icell) - from_cell) + (sign(rel_cell) * 0.5) + 0.5) * delta;
@@ -150,79 +157,66 @@ bool trace_ray(vec3 p_from, vec3 p_to
while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(params.grid_size))) && iters < 1000) {
uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy;
if (cell_data.x > 0) { //triangles here
- bool hit = false;
-#if defined(MODE_UNOCCLUDE)
- bool hit_backface = false;
-#endif
+ uint hit = RAY_MISS;
float best_distance = 1e20;
for (uint i = 0; i < cell_data.x; i++) {
uint tidx = grid_indices.data[cell_data.y + i];
//Ray-Box test
- vec3 t0 = (boxes.data[tidx].min_bounds - p_from) * inv_dir;
- vec3 t1 = (boxes.data[tidx].max_bounds - p_from) * inv_dir;
+ Triangle triangle = triangles.data[tidx];
+ vec3 t0 = (triangle.min_bounds - p_from) * inv_dir;
+ vec3 t1 = (triangle.max_bounds - p_from) * inv_dir;
vec3 tmin = min(t0, t1), tmax = max(t0, t1);
- if (max(tmin.x, max(tmin.y, tmin.z)) <= min(tmax.x, min(tmax.y, tmax.z))) {
+ if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) {
continue; //ray box failed
}
//prepare triangle vertices
- vec3 vtx0 = vertices.data[triangles.data[tidx].indices.x].position;
- vec3 vtx1 = vertices.data[triangles.data[tidx].indices.y].position;
- vec3 vtx2 = vertices.data[triangles.data[tidx].indices.z].position;
-#if defined(MODE_UNOCCLUDE)
+ vec3 vtx0 = vertices.data[triangle.indices.x].position;
+ vec3 vtx1 = vertices.data[triangle.indices.y].position;
+ vec3 vtx2 = vertices.data[triangle.indices.z].position;
+#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2)));
bool backface = dot(normal, dir) >= 0.0;
#endif
+
float distance;
vec3 barycentric;
if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) {
#ifdef MODE_DIRECT_LIGHT
- return true; //any hit good
+ return RAY_ANY; //any hit good
#endif
-#if defined(MODE_UNOCCLUDE)
+#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
if (!backface) {
// the case of meshes having both a front and back face in the same plane is more common than
// expected, so if this is a front-face, bias it closer to the ray origin, so it always wins over the back-face
distance = max(params.bias, distance - params.bias);
}
- hit = true;
-
if (distance < best_distance) {
- hit_backface = backface;
+ hit = backface ? RAY_BACK : RAY_FRONT;
best_distance = distance;
+#if defined(MODE_UNOCCLUDE)
r_distance = distance;
r_normal = normal;
- }
-
#endif
-
#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
-
- hit = true;
- if (distance < best_distance) {
- best_distance = distance;
r_triangle = tidx;
r_barycentric = barycentric;
+#endif
}
#endif
}
}
-#if defined(MODE_UNOCCLUDE)
+#if defined(MODE_UNOCCLUDE) || defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
- if (hit) {
- return hit_backface;
- }
-#endif
-#if defined(MODE_BOUNCE_LIGHT) || defined(MODE_LIGHT_PROBES)
- if (hit) {
- return true;
+ if (hit != RAY_MISS) {
+ return hit;
}
#endif
}
@@ -238,22 +232,42 @@ bool trace_ray(vec3 p_from, vec3 p_to
iters++;
}
- return false;
+ return RAY_MISS;
+}
+
+// https://www.reedbeta.com/blog/hash-functions-for-gpu-rendering/
+uint hash(uint value) {
+ uint state = value * 747796405u + 2891336453u;
+ uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
+ return (word >> 22u) ^ word;
+}
+
+uint random_seed(ivec3 seed) {
+ return hash(seed.x ^ hash(seed.y ^ hash(seed.z)));
+}
+
+// generates a random value in range [0.0, 1.0)
+float randomize(inout uint value) {
+ value = hash(value);
+ return float(value / 4294967296.0);
}
const float PI = 3.14159265f;
-const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0));
-
-vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) {
- float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count));
- float theta = float(p_index) * GOLDEN_ANGLE + p_offset;
- float y = cos(r * PI * 0.5);
- float l = sin(r * PI * 0.5);
- return vec3(l * cos(theta), l * sin(theta), y);
+
+// http://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.4.pdf (chapter 15)
+vec3 generate_hemisphere_uniform_direction(inout uint noise) {
+ float noise1 = randomize(noise);
+ float noise2 = randomize(noise) * 2.0 * PI;
+
+ float factor = sqrt(1 - (noise1 * noise1));
+ return vec3(factor * cos(noise2), factor * sin(noise2), noise1);
}
-float quick_hash(vec2 pos) {
- return fract(sin(dot(pos * 19.19, vec2(49.5791, 97.413))) * 49831.189237);
+vec3 generate_hemisphere_cosine_weighted_direction(inout uint noise) {
+ float noise1 = randomize(noise);
+ float noise2 = randomize(noise) * 2.0 * PI;
+
+ return vec3(sqrt(noise1) * cos(noise2), sqrt(noise1) * sin(noise2), sqrt(1.0 - noise1));
}
float get_omni_attenuation(float distance, float inv_range, float decay) {
@@ -338,7 +352,7 @@ void main() {
continue; //no need to do anything
}
- if (!trace_ray(position + light_dir * params.bias, light_pos)) {
+ 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;
@@ -409,14 +423,17 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
#endif
vec3 light_average = vec3(0.0);
+ float active_rays = 0.0;
+ uint noise = random_seed(ivec3(params.ray_from, atlas_pos));
for (uint i = params.ray_from; i < params.ray_to; i++) {
- vec3 ray_dir = normal_mat * vogel_hemisphere(i, params.ray_count, quick_hash(vec2(atlas_pos)));
+ vec3 ray_dir = normal_mat * generate_hemisphere_cosine_weighted_direction(noise);
uint tidx;
vec3 barycentric;
vec3 light = vec3(0.0);
- if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
+ uint trace_result = trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric);
+ if (trace_result == RAY_FRONT) {
//hit a triangle
vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
@@ -424,20 +441,24 @@ void main() {
vec3 uvw = vec3(barycentric.x * uv0 + barycentric.y * uv1 + barycentric.z * uv2, float(triangles.data[tidx].slice));
light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
- } else if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
- // Did not hit a triangle, reach out for the sky
- vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
+ active_rays += 1.0;
+ } else if (trace_result == RAY_MISS) {
+ if (params.env_transform[0][3] == 0.0) { // Use env_transform[0][3] to indicate when we are computing the first bounce
+ // Did not hit a triangle, reach out for the sky
+ vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
- vec2 st = vec2(
- atan(sky_dir.x, sky_dir.z),
- acos(sky_dir.y));
+ vec2 st = vec2(
+ atan(sky_dir.x, sky_dir.z),
+ acos(sky_dir.y));
- if (st.x < 0.0)
- st.x += PI * 2.0;
+ if (st.x < 0.0)
+ st.x += PI * 2.0;
- st /= vec2(PI * 2.0, PI);
+ st /= vec2(PI * 2.0, PI);
- light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+ light = textureLod(sampler2D(environment, linear_sampler), st, 0.0).rgb;
+ }
+ active_rays += 1.0;
}
light_average += light;
@@ -461,7 +482,9 @@ void main() {
if (params.ray_from == 0) {
light_total = vec3(0.0);
} else {
- light_total = imageLoad(bounce_accum, ivec3(atlas_pos, params.atlas_slice)).rgb;
+ vec4 accum = imageLoad(bounce_accum, ivec3(atlas_pos, params.atlas_slice));
+ light_total = accum.rgb;
+ active_rays += accum.a;
}
light_total += light_average;
@@ -476,7 +499,9 @@ void main() {
#endif
if (params.ray_to == params.ray_count) {
- light_total /= float(params.ray_count);
+ if (active_rays > 0) {
+ light_total /= active_rays;
+ }
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, 1.0));
#ifndef USE_SH_LIGHTMAPS
vec4 accum = imageLoad(accum_light, ivec3(atlas_pos, params.atlas_slice));
@@ -484,7 +509,7 @@ void main() {
imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), accum);
#endif
} else {
- imageStore(bounce_accum, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, 1.0));
+ imageStore(bounce_accum, ivec3(atlas_pos, params.atlas_slice), vec4(light_total, active_rays));
}
#endif
@@ -517,7 +542,7 @@ void main() {
float d;
vec3 norm;
- if (trace_ray(base_pos, ray_to, d, norm)) {
+ if (trace_ray(base_pos, ray_to, d, norm) == RAY_BACK) {
if (d < min_d) {
vertex_pos = base_pos + rays[i] * d + norm * params.bias * 10.0; //this bias needs to be greater than the regular bias, because otherwise later, rays will go the other side when pointing back.
min_d = d;
@@ -546,8 +571,9 @@ void main() {
vec4(0.0),
vec4(0.0));
+ uint noise = random_seed(ivec3(params.ray_from, probe_index, 49502741 /* some prime */));
for (uint i = params.ray_from; i < params.ray_to; i++) {
- vec3 ray_dir = vogel_hemisphere(i, params.ray_count, quick_hash(vec2(float(probe_index), 0.0)));
+ vec3 ray_dir = generate_hemisphere_uniform_direction(noise);
if (bool(i & 1)) {
//throw to both sides, so alternate them
ray_dir.z *= -1.0;
@@ -557,7 +583,8 @@ void main() {
vec3 barycentric;
vec3 light;
- if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
+ uint trace_result = trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric);
+ if (trace_result == RAY_FRONT) {
vec2 uv0 = vertices.data[triangles.data[tidx].indices.x].uv;
vec2 uv1 = vertices.data[triangles.data[tidx].indices.y].uv;
vec2 uv2 = vertices.data[triangles.data[tidx].indices.z].uv;
@@ -565,7 +592,7 @@ void main() {
light = textureLod(sampler2DArray(source_light, linear_sampler), uvw, 0.0).rgb;
light += textureLod(sampler2DArray(source_direct_light, linear_sampler), uvw, 0.0).rgb;
- } else {
+ } else if (trace_result == RAY_MISS) {
//did not hit a triangle, reach out for the sky
vec3 sky_dir = normalize(mat3(params.env_transform) * ray_dir);
diff --git a/modules/lightmapper_rd/lm_raster.glsl b/modules/lightmapper_rd/lm_raster.glsl
index 55ca193cc1..a86968a4f3 100644
--- a/modules/lightmapper_rd/lm_raster.glsl
+++ b/modules/lightmapper_rd/lm_raster.glsl
@@ -12,6 +12,7 @@ layout(location = 2) out vec2 uv_interp;
layout(location = 3) out vec3 barycentric;
layout(location = 4) flat out uvec3 vertex_indices;
layout(location = 5) flat out vec3 face_normal;
+layout(location = 6) flat out uint fragment_action;
layout(push_constant, binding = 0, std430) uniform Params {
vec2 atlas_size;
@@ -49,6 +50,14 @@ void main() {
face_normal = -normalize(cross((vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.y].position), (vertices.data[vertex_indices.x].position - vertices.data[vertex_indices.z].position)));
+ {
+ const float FLAT_THRESHOLD = 0.99;
+ const vec3 norm_a = vec3(vertices.data[vertex_indices.x].normal_xy, vertices.data[vertex_indices.x].normal_z);
+ const vec3 norm_b = vec3(vertices.data[vertex_indices.y].normal_xy, vertices.data[vertex_indices.y].normal_z);
+ const vec3 norm_c = vec3(vertices.data[vertex_indices.z].normal_xy, vertices.data[vertex_indices.z].normal_z);
+ fragment_action = (dot(norm_a, norm_b) < FLAT_THRESHOLD || dot(norm_a, norm_c) < FLAT_THRESHOLD || dot(norm_b, norm_c) < FLAT_THRESHOLD) ? FA_SMOOTHEN_POSITION : FA_NONE;
+ }
+
gl_Position = vec4((uv_interp + params.uv_offset) * 2.0 - 1.0, 0.0001, 1.0);
}
@@ -78,6 +87,7 @@ layout(location = 2) in vec2 uv_interp;
layout(location = 3) in vec3 barycentric;
layout(location = 4) in flat uvec3 vertex_indices;
layout(location = 5) in flat vec3 face_normal;
+layout(location = 6) in flat uint fragment_action;
layout(location = 0) out vec4 position;
layout(location = 1) out vec4 normal;
@@ -86,7 +96,7 @@ layout(location = 2) out vec4 unocclude;
void main() {
vec3 vertex_pos = vertex_interp;
- {
+ if (fragment_action == FA_SMOOTHEN_POSITION) {
// smooth out vertex position by interpolating its projection in the 3 normal planes (normal plane is created by vertex pos and normal)
// because we don't want to interpolate inwards, normals found pointing inwards are pushed out.
vec3 pos_a = vertices.data[vertex_indices.x].position;
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 2522f1bb11..9d985e16d4 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -310,7 +310,7 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
default_certs = memnew(X509CertificateMbedTLS);
ERR_FAIL_COND(default_certs == nullptr);
- if (p_path != "") {
+ if (!p_path.is_empty()) {
// Use certs defined in project settings.
default_certs->load(p_path);
}
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h
index 6554c74a21..92e6ab88c4 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.h
+++ b/modules/mbedtls/packet_peer_mbed_dtls.h
@@ -59,8 +59,6 @@ protected:
Ref<SSLContextMbedTLS> ssl_ctx;
mbedtls_timing_delay_context timer;
- static void _bind_methods();
-
Error _do_handshake();
int _set_cookie();
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h
index 1b55a54a10..5692dec1b6 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/ssl_context_mbedtls.h
@@ -67,8 +67,6 @@ class SSLContextMbedTLS : public RefCounted {
protected:
bool inited = false;
- static PackedByteArray _read_file(String p_path);
-
public:
static void print_mbedtls_error(int p_ret);
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index b89d7fb238..407479e3cc 100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -50,8 +50,6 @@ private:
protected:
Ref<SSLContextMbedTLS> ssl_ctx;
- static void _bind_methods();
-
Error _do_handshake();
public:
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index fb741f6266..17406b7263 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -112,7 +112,7 @@ void AudioStreamPlaybackMP3::seek(float p_time) {
}
frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
- mp3dec_ex_seek(mp3d, frames_mixed * mp3_stream->channels);
+ mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
}
AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
@@ -125,7 +125,7 @@ AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
Ref<AudioStreamPlaybackMP3> mp3s;
- ERR_FAIL_COND_V_MSG(data == nullptr, mp3s,
+ ERR_FAIL_COND_V_MSG(data.is_empty(), mp3s,
"This AudioStreamMP3 does not have an audio file assigned "
"to it. AudioStreamMP3 should not be created from the "
"inspector or with `.new()`. Instead, load an audio file.");
@@ -134,7 +134,7 @@ Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
mp3s->mp3_stream = Ref<AudioStreamMP3>(this);
mp3s->mp3d = (mp3dec_ex_t *)memalloc(sizeof(mp3dec_ex_t));
- int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, (const uint8_t *)data, data_len, MP3D_SEEK_TO_SAMPLE);
+ int errorcode = mp3dec_ex_open_buf(mp3s->mp3d, data.ptr(), data_len, MP3D_SEEK_TO_SAMPLE);
mp3s->frames_mixed = 0;
mp3s->active = false;
@@ -152,11 +152,7 @@ String AudioStreamMP3::get_stream_name() const {
}
void AudioStreamMP3::clear_data() {
- if (data) {
- memfree(data);
- data = nullptr;
- data_len = 0;
- }
+ data.clear();
}
void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
@@ -165,7 +161,7 @@ void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
mp3dec_ex_t mp3d;
int err = mp3dec_ex_open_buf(&mp3d, src_datar, src_data_len, MP3D_SEEK_TO_SAMPLE);
- ERR_FAIL_COND(err != 0);
+ ERR_FAIL_COND_MSG(err || mp3d.info.hz == 0, "Failed to decode mp3 file. Make sure it is a valid mp3 audio file.");
channels = mp3d.info.channels;
sample_rate = mp3d.info.hz;
@@ -175,23 +171,13 @@ void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
clear_data();
- data = memalloc(src_data_len);
- memcpy(data, src_datar, src_data_len);
+ data.resize(src_data_len);
+ memcpy(data.ptrw(), src_datar, src_data_len);
data_len = src_data_len;
}
Vector<uint8_t> AudioStreamMP3::get_data() const {
- Vector<uint8_t> vdata;
-
- if (data_len && data) {
- vdata.resize(data_len);
- {
- uint8_t *w = vdata.ptrw();
- memcpy(w, data, data_len);
- }
- }
-
- return vdata;
+ return data;
}
void AudioStreamMP3::set_loop(bool p_enable) {
@@ -214,6 +200,10 @@ float AudioStreamMP3::get_length() const {
return length;
}
+bool AudioStreamMP3::is_monophonic() const {
+ return false;
+}
+
void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamMP3::set_data);
ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamMP3::get_data);
@@ -224,9 +214,9 @@ void AudioStreamMP3::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamMP3::set_loop_offset);
ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamMP3::get_loop_offset);
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop", "has_loop");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_loop_offset", "get_loop_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
}
AudioStreamMP3::AudioStreamMP3() {
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
index 5dd88779f8..e3adfe683b 100644
--- a/modules/minimp3/audio_stream_mp3.h
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -75,7 +75,7 @@ class AudioStreamMP3 : public AudioStream {
friend class AudioStreamPlaybackMP3;
- void *data = nullptr;
+ PackedByteArray data;
uint32_t data_len = 0;
float sample_rate = 1.0;
@@ -103,6 +103,8 @@ public:
virtual float get_length() const override;
+ virtual bool is_monophonic() const override;
+
AudioStreamMP3();
virtual ~AudioStreamMP3();
};
diff --git a/modules/minimp3/doc_classes/AudioStreamMP3.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml
index 5507329a18..e4f56614ee 100644
--- a/modules/minimp3/doc_classes/AudioStreamMP3.xml
+++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
Contains the audio data in bytes.
@@ -21,6 +19,4 @@
Time in seconds at which the stream starts after being looped.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index dc360c12ba..b2a755e23b 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_option, const Map<StringName, Variant> &p_options) const {
+bool ResourceImporterMP3::get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const {
return true;
}
@@ -66,7 +66,7 @@ String ResourceImporterMP3::get_preset_name(int p_idx) const {
return String();
}
-void ResourceImporterMP3::get_import_options(List<ImportOption> *r_options, int p_preset) const {
+void ResourceImporterMP3::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "loop_offset"), 0));
}
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 71b51887a2..356ec77d22 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -47,8 +47,8 @@ public:
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) 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 Error import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index 120535bd41..df099f2c98 100644
--- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml
+++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
@@ -15,8 +15,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="display_to_lens" type="float" setter="set_display_to_lens" getter="get_display_to_lens" default="4.0">
The distance between the display and the lenses inside of the device in centimeters.
@@ -39,7 +37,6 @@
<member name="oversample" type="float" setter="set_oversample" getter="get_oversample" default="1.5">
The oversample setting. Because of the lens distortion we have to render our buffers at a higher resolution then the screen can natively handle. A value between 1.5 and 2.0 often provides good results but at the cost of performance.
</member>
+ <member name="xr_play_area_mode" type="int" setter="set_play_area_mode" getter="get_play_area_mode" overrides="XRInterface" enum="XRInterface.PlayAreaMode" default="1" />
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp
index 12bb0e5b44..ba7353ace2 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -126,6 +126,8 @@ void MobileVRInterface::set_position_from_sensors() {
// 9dof is a misleading marketing term coming from 3 accelerometer axis + 3 gyro axis + 3 magnetometer axis = 9 axis
// but in reality this only offers 3 dof (yaw, pitch, roll) orientation
+ Basis orientation;
+
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
uint64_t ticks_elapsed = ticks - last_ticks;
float delta_time = (double)ticks_elapsed / 1000000.0;
@@ -207,8 +209,8 @@ void MobileVRInterface::set_position_from_sensors() {
};
};
- // JIC
- orientation.orthonormalize();
+ // and copy to our head transform
+ head_transform.basis = orientation.orthonormalized();
last_ticks = ticks;
};
@@ -244,59 +246,59 @@ void MobileVRInterface::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "k2", PROPERTY_HINT_RANGE, "0.1,10.0,0.0001"), "set_k2", "get_k2");
}
-void MobileVRInterface::set_eye_height(const real_t p_eye_height) {
+void MobileVRInterface::set_eye_height(const double p_eye_height) {
eye_height = p_eye_height;
}
-real_t MobileVRInterface::get_eye_height() const {
+double MobileVRInterface::get_eye_height() const {
return eye_height;
}
-void MobileVRInterface::set_iod(const real_t p_iod) {
+void MobileVRInterface::set_iod(const double p_iod) {
intraocular_dist = p_iod;
};
-real_t MobileVRInterface::get_iod() const {
+double MobileVRInterface::get_iod() const {
return intraocular_dist;
};
-void MobileVRInterface::set_display_width(const real_t p_display_width) {
+void MobileVRInterface::set_display_width(const double p_display_width) {
display_width = p_display_width;
};
-real_t MobileVRInterface::get_display_width() const {
+double MobileVRInterface::get_display_width() const {
return display_width;
};
-void MobileVRInterface::set_display_to_lens(const real_t p_display_to_lens) {
+void MobileVRInterface::set_display_to_lens(const double p_display_to_lens) {
display_to_lens = p_display_to_lens;
};
-real_t MobileVRInterface::get_display_to_lens() const {
+double MobileVRInterface::get_display_to_lens() const {
return display_to_lens;
};
-void MobileVRInterface::set_oversample(const real_t p_oversample) {
+void MobileVRInterface::set_oversample(const double p_oversample) {
oversample = p_oversample;
};
-real_t MobileVRInterface::get_oversample() const {
+double MobileVRInterface::get_oversample() const {
return oversample;
};
-void MobileVRInterface::set_k1(const real_t p_k1) {
+void MobileVRInterface::set_k1(const double p_k1) {
k1 = p_k1;
};
-real_t MobileVRInterface::get_k1() const {
+double MobileVRInterface::get_k1() const {
return k1;
};
-void MobileVRInterface::set_k2(const real_t p_k2) {
+void MobileVRInterface::set_k2(const double p_k2) {
k2 = p_k2;
};
-real_t MobileVRInterface::get_k2() const {
+double MobileVRInterface::get_k2() const {
return k2;
};
@@ -318,7 +320,7 @@ bool MobileVRInterface::initialize() {
ERR_FAIL_NULL_V(xr_server, false);
if (!initialized) {
- // reset our sensor data and orientation
+ // reset our sensor data
mag_count = 0;
has_gyro = false;
sensor_first = true;
@@ -326,9 +328,15 @@ bool MobileVRInterface::initialize() {
mag_next_max = Vector3(-10000, -10000, -10000);
mag_current_min = Vector3(0, 0, 0);
mag_current_max = Vector3(0, 0, 0);
+ head_transform.basis = Basis();
+ head_transform.origin = Vector3(0.0, eye_height, 0.0);
- // reset our orientation
- orientation = Basis();
+ // we must create a tracker for our head
+ head.instantiate();
+ head->set_tracker_type(XRServer::TRACKER_HEAD);
+ head->set_tracker_name("head");
+ head->set_tracker_desc("Players head");
+ xr_server->add_tracker(head);
// make this our primary interface
xr_server->set_primary_interface(this);
@@ -343,16 +351,38 @@ bool MobileVRInterface::initialize() {
void MobileVRInterface::uninitialize() {
if (initialized) {
+ // do any cleanup here...
XRServer *xr_server = XRServer::get_singleton();
- if (xr_server != nullptr && xr_server->get_primary_interface() == this) {
- // no longer our primary interface
- xr_server->set_primary_interface(nullptr);
+ if (xr_server != nullptr) {
+ if (head.is_valid()) {
+ xr_server->remove_tracker(head);
+
+ head.unref();
+ }
+
+ if (xr_server->get_primary_interface() == this) {
+ // no longer our primary interface
+ xr_server->set_primary_interface(nullptr);
+ }
}
initialized = false;
};
};
+bool MobileVRInterface::supports_play_area_mode(XRInterface::PlayAreaMode p_mode) {
+ // This interface has no positional tracking so fix this to 3DOF
+ return p_mode == XR_PLAY_AREA_3DOF;
+}
+
+XRInterface::PlayAreaMode MobileVRInterface::get_play_area_mode() const {
+ return XR_PLAY_AREA_3DOF;
+}
+
+bool MobileVRInterface::set_play_area_mode(XRInterface::PlayAreaMode p_mode) {
+ return p_mode == XR_PLAY_AREA_3DOF;
+}
+
Size2 MobileVRInterface::get_render_target_size() {
_THREAD_SAFE_METHOD_
@@ -377,11 +407,10 @@ Transform3D MobileVRInterface::get_camera_transform() {
float world_scale = xr_server->get_world_scale();
// just scale our origin point of our transform
- Transform3D hmd_transform;
- hmd_transform.basis = orientation;
- hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0);
+ Transform3D _head_transform = head_transform;
+ _head_transform.origin *= world_scale;
- transform_for_eye = (xr_server->get_reference_frame()) * hmd_transform;
+ transform_for_eye = (xr_server->get_reference_frame()) * _head_transform;
}
return transform_for_eye;
@@ -409,11 +438,10 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra
};
// just scale our origin point of our transform
- Transform3D hmd_transform;
- hmd_transform.basis = orientation;
- hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0);
+ Transform3D _head_transform = head_transform;
+ _head_transform.origin *= world_scale;
- transform_for_eye = p_cam_transform * (xr_server->get_reference_frame()) * hmd_transform * transform_for_eye;
+ transform_for_eye = p_cam_transform * (xr_server->get_reference_frame()) * _head_transform * transform_for_eye;
} else {
// huh? well just return what we got....
transform_for_eye = p_cam_transform;
@@ -422,7 +450,7 @@ Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Tra
return transform_for_eye;
};
-CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
+CameraMatrix MobileVRInterface::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
_THREAD_SAFE_METHOD_
CameraMatrix eye;
@@ -455,16 +483,16 @@ Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const
blit.lens_distortion.aspect_ratio = aspect;
// left eye
- blit.rect = p_screen_rect;
- blit.rect.size.width *= 0.5;
+ blit.dst_rect = p_screen_rect;
+ blit.dst_rect.size.width *= 0.5;
blit.multi_view.layer = 0;
blit.lens_distortion.eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
// right eye
- blit.rect = p_screen_rect;
- blit.rect.size.width *= 0.5;
- blit.rect.position.x = blit.rect.size.width;
+ blit.dst_rect = p_screen_rect;
+ blit.dst_rect.size.width *= 0.5;
+ blit.dst_rect.position.x = blit.dst_rect.size.width;
blit.multi_view.layer = 1;
blit.lens_distortion.eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0);
blit_to_screen.push_back(blit);
@@ -476,7 +504,16 @@ void MobileVRInterface::process() {
_THREAD_SAFE_METHOD_
if (initialized) {
+ // update our head transform orientation
set_position_from_sensors();
+
+ // update our head transform position (should be constant)
+ head_transform.origin = Vector3(0.0, eye_height, 0.0);
+
+ if (head.is_valid()) {
+ // Set our head position, note in real space, reference frame and world scale is applied later
+ head->set_pose("default", head_transform, Vector3(), Vector3());
+ }
};
};
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index 48b76ec187..b5bf966247 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -53,20 +53,23 @@ class MobileVRInterface : public XRInterface {
private:
bool initialized = false;
XRInterface::TrackingStatus tracking_state;
- Basis orientation;
// Just set some defaults for these. At some point we need to look at adding a lookup table for common device + headset combos and/or support reading cardboard QR codes
- float eye_height = 1.85;
+ double eye_height = 1.85;
uint64_t last_ticks = 0;
- real_t intraocular_dist = 6.0;
- real_t display_width = 14.5;
- real_t display_to_lens = 4.0;
- real_t oversample = 1.5;
+ double intraocular_dist = 6.0;
+ double display_width = 14.5;
+ double display_to_lens = 4.0;
+ double oversample = 1.5;
- real_t k1 = 0.215;
- real_t k2 = 0.215;
- real_t aspect = 1.0;
+ double k1 = 0.215;
+ double k2 = 0.215;
+ double aspect = 1.0;
+
+ // at a minimum we need a tracker for our head
+ Ref<XRPositionalTracker> head;
+ Transform3D head_transform;
/*
logic for processing our sensor data, this was originally in our positional tracker logic but I think
@@ -110,26 +113,26 @@ protected:
static void _bind_methods();
public:
- void set_eye_height(const real_t p_eye_height);
- real_t get_eye_height() const;
+ void set_eye_height(const double p_eye_height);
+ double get_eye_height() const;
- void set_iod(const real_t p_iod);
- real_t get_iod() const;
+ void set_iod(const double p_iod);
+ double get_iod() const;
- void set_display_width(const real_t p_display_width);
- real_t get_display_width() const;
+ void set_display_width(const double p_display_width);
+ double get_display_width() const;
- void set_display_to_lens(const real_t p_display_to_lens);
- real_t get_display_to_lens() const;
+ void set_display_to_lens(const double p_display_to_lens);
+ double get_display_to_lens() const;
- void set_oversample(const real_t p_oversample);
- real_t get_oversample() const;
+ void set_oversample(const double p_oversample);
+ double get_oversample() const;
- void set_k1(const real_t p_k1);
- real_t get_k1() const;
+ void set_k1(const double p_k1);
+ double get_k1() const;
- void set_k2(const real_t p_k2);
- real_t get_k2() const;
+ void set_k2(const double p_k2);
+ double get_k2() const;
virtual StringName get_name() const override;
virtual uint32_t get_capabilities() const override;
@@ -140,11 +143,15 @@ public:
virtual bool initialize() override;
virtual void uninitialize() override;
+ virtual bool supports_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
+ virtual XRInterface::PlayAreaMode get_play_area_mode() const override;
+ virtual bool set_play_area_mode(XRInterface::PlayAreaMode p_mode) override;
+
virtual Size2 get_render_target_size() override;
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
+ virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 47d1fe482c..233c16531a 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -32,15 +32,30 @@
#include "mobile_vr_interface.h"
+Ref<MobileVRInterface> mobile_vr;
+
void register_mobile_vr_types() {
GDREGISTER_CLASS(MobileVRInterface);
if (XRServer::get_singleton()) {
- Ref<MobileVRInterface> mobile_vr;
mobile_vr.instantiate();
XRServer::get_singleton()->add_interface(mobile_vr);
}
}
void unregister_mobile_vr_types() {
+ if (mobile_vr.is_valid()) {
+ // uninitialise our interface if it is initialised
+ if (mobile_vr->is_initialized()) {
+ mobile_vr->uninitialize();
+ }
+
+ // unregister our interface from the XR server
+ if (XRServer::get_singleton()) {
+ XRServer::get_singleton()->remove_interface(mobile_vr);
+ }
+
+ // and release
+ mobile_vr.unref();
+ }
}
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index 3b94949470..95c959235c 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -49,6 +49,7 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
env_mono.add_source_files(env.modules_sources, "*.cpp")
env_mono.add_source_files(env.modules_sources, "glue/*.cpp")
+env_mono.add_source_files(env.modules_sources, "glue/mono_glue.gen.cpp")
env_mono.add_source_files(env.modules_sources, "mono_gd/*.cpp")
env_mono.add_source_files(env.modules_sources, "utils/*.cpp")
@@ -57,6 +58,8 @@ env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.cpp")
if env["platform"] in ["osx", "iphone"]:
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.mm")
env_mono.add_source_files(env.modules_sources, "mono_gd/support/*.m")
+elif env["platform"] == "android":
+ env_mono.add_source_files(env.modules_sources, "mono_gd/android_mono_config.gen.cpp")
if env["tools"]:
env_mono.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/mono/SdkPackageVersions.props b/modules/mono/SdkPackageVersions.props
index df3ebe581c..bdec051625 100644
--- a/modules/mono/SdkPackageVersions.props
+++ b/modules/mono/SdkPackageVersions.props
@@ -1,6 +1,7 @@
<Project>
<PropertyGroup>
- <PackageVersion_Godot_NET_Sdk>4.0.0-dev5</PackageVersion_Godot_NET_Sdk>
- <PackageVersion_Godot_SourceGenerators>4.0.0-dev2</PackageVersion_Godot_SourceGenerators>
+ <PackageFloatingVersion_Godot>4.0.*-*</PackageFloatingVersion_Godot>
+ <PackageVersion_Godot_NET_Sdk>4.0.0-dev6</PackageVersion_Godot_NET_Sdk>
+ <PackageVersion_Godot_SourceGenerators>4.0.0-dev3</PackageVersion_Godot_SourceGenerators>
</PropertyGroup>
</Project>
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index 28494bff6e..1920ef1c1a 100644
--- a/modules/mono/build_scripts/make_android_mono_config.py
+++ b/modules/mono/build_scripts/make_android_mono_config.py
@@ -8,7 +8,9 @@ def generate_compressed_config(config_src, output_dir):
decompr_size = len(buf)
import zlib
- buf = zlib.compress(buf)
+ # Use maximum zlib compression level to further reduce file size
+ # (at the cost of initial build times).
+ buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION)
compr_size = len(buf)
bytes_seq_str = ""
diff --git a/modules/mono/build_scripts/mono_reg_utils.py b/modules/mono/build_scripts/mono_reg_utils.py
index 0ec7e2f433..93a66ebf6f 100644
--- a/modules/mono/build_scripts/mono_reg_utils.py
+++ b/modules/mono/build_scripts/mono_reg_utils.py
@@ -2,7 +2,6 @@ import os
import platform
if os.name == "nt":
- import sys
import winreg
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 4c851a2989..df02d9a309 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -2,7 +2,7 @@ supported_platforms = ["windows", "osx", "linuxbsd", "server", "android", "haiku
def can_build(env, platform):
- return True
+ return not env["arch"].startswith("rv")
def configure(env):
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 978093b7d5..0ceb45d425 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -43,8 +43,10 @@
#include "core/os/thread.h"
#ifdef TOOLS_ENABLED
+#include "core/os/keyboard.h"
#include "editor/bindings_generator.h"
#include "editor/editor_node.h"
+#include "editor/editor_settings.h"
#include "editor/node_dock.h"
#endif
@@ -311,22 +313,22 @@ void CSharpLanguage::get_reserved_words(List<String> *p_words) const {
bool CSharpLanguage::is_control_flow_keyword(String p_keyword) const {
return p_keyword == "break" ||
- p_keyword == "case" ||
- p_keyword == "catch" ||
- p_keyword == "continue" ||
- p_keyword == "default" ||
- p_keyword == "do" ||
- p_keyword == "else" ||
- p_keyword == "finally" ||
- p_keyword == "for" ||
- p_keyword == "foreach" ||
- p_keyword == "goto" ||
- p_keyword == "if" ||
- p_keyword == "return" ||
- p_keyword == "switch" ||
- p_keyword == "throw" ||
- p_keyword == "try" ||
- p_keyword == "while";
+ p_keyword == "case" ||
+ p_keyword == "catch" ||
+ p_keyword == "continue" ||
+ p_keyword == "default" ||
+ p_keyword == "do" ||
+ p_keyword == "else" ||
+ p_keyword == "finally" ||
+ p_keyword == "for" ||
+ p_keyword == "foreach" ||
+ p_keyword == "goto" ||
+ p_keyword == "if" ||
+ p_keyword == "return" ||
+ p_keyword == "switch" ||
+ p_keyword == "throw" ||
+ p_keyword == "try" ||
+ p_keyword == "while";
}
void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
@@ -337,7 +339,7 @@ void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
void CSharpLanguage::get_string_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("' '"); // character literal
p_delimiters->push_back("\" \""); // regular string literal
- // Verbatim string literals (`@" "`) don't render correctly, so don't highlight them.
+ p_delimiters->push_back("@\" \""); // verbatim string literal
// Generic string highlighting suffices as a workaround for now.
}
@@ -786,11 +788,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
- String appname = ProjectSettings::get_singleton()->get("application/config/name");
- String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.is_empty()) {
- appname_safe = "UnnamedProject";
- }
+ String appname_safe = ProjectSettings::get_singleton()->get_safe_project_name();
appname_safe += ".dll";
@@ -1353,6 +1351,7 @@ void CSharpLanguage::_editor_init_callback() {
// Enable it as a plugin
EditorNode::add_editor_plugin(godotsharp_editor);
+ ED_SHORTCUT("mono/build_solution", TTR("Build Solution"), KeyModifierMask::ALT | Key::B);
godotsharp_editor->enable_plugin();
get_singleton()->godotsharp_editor = godotsharp_editor;
@@ -1657,7 +1656,7 @@ bool CSharpInstance::set(const StringName &p_name, const Variant &p_value) {
GDMonoProperty *property = top->get_property(p_name);
if (property) {
- property->set_value(mono_object, GDMonoMarshal::variant_to_mono_object(p_value, property->get_type()));
+ property->set_value_from_variant(mono_object, p_value);
return true;
}
@@ -1810,8 +1809,9 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (const KeyValue<StringName, PropertyInfo> &E : script->member_info) {
- p_properties->push_back(E.value);
+ List<PropertyInfo> props;
+ for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = script->member_info.front(); E; E = E.next()) {
+ props.push_front(E.value());
}
// Call _get_property_list
@@ -1834,9 +1834,8 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
if (ret) {
Array array = Array(GDMonoMarshal::mono_object_to_variant(ret));
for (int i = 0, size = array.size(); i < size; i++) {
- p_properties->push_back(PropertyInfo::from_dict(array.get(i)));
+ props.push_back(PropertyInfo::from_dict(array.get(i)));
}
- return;
}
break;
@@ -1844,6 +1843,10 @@ void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
top = top->get_parent_class();
}
+
+ for (const PropertyInfo &prop : props) {
+ p_properties->push_back(prop);
+ }
}
Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
@@ -1861,6 +1864,29 @@ Variant::Type CSharpInstance::get_property_type(const StringName &p_name, bool *
return Variant::NIL;
}
+void CSharpInstance::get_method_list(List<MethodInfo> *p_list) const {
+ if (!script->is_valid() || !script->script_class) {
+ return;
+ }
+
+ GD_MONO_SCOPE_THREAD_ATTACH;
+
+ // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
+ GDMonoClass *top = script->script_class;
+
+ while (top && top != script->native) {
+ const Vector<GDMonoMethod *> &methods = top->get_all_methods();
+ for (int i = 0; i < methods.size(); ++i) {
+ MethodInfo minfo = methods[i]->get_method_info();
+ if (minfo.name != CACHED_STRING_NAME(dotctor)) {
+ p_list->push_back(minfo);
+ }
+ }
+
+ top = top->get_parent_class();
+ }
+}
+
bool CSharpInstance::has_method(const StringName &p_method) const {
if (!script.is_valid()) {
return false;
@@ -2124,7 +2150,7 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
-const Vector<MultiplayerAPI::RPCConfig> CSharpInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> CSharpInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
@@ -2866,12 +2892,24 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
ERR_FAIL_COND_V_MSG(elem_variant_type == Variant::NIL, -1, "Unknown array element type.");
- int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
+ bool preset_hint = false;
+ if (elem_variant_type == Variant::STRING) {
+ MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
+ if (PropertyHint(CACHED_FIELD(ExportAttribute, hint)->get_int_value(attr)) == PROPERTY_HINT_ENUM) {
+ r_hint_string = itos(elem_variant_type) + "/" + itos(PROPERTY_HINT_ENUM) + ":" + CACHED_FIELD(ExportAttribute, hintString)->get_string_value(attr);
+ preset_hint = true;
+ }
+ }
+
+ if (!preset_hint) {
+ int hint_res = _try_get_member_export_hint(p_member, elem_type, elem_variant_type, /* allow_generics: */ false, elem_hint, elem_hint_string);
- ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
+ ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
+
+ // Format: type/hint:hint_string
+ r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
+ }
- // Format: type/hint:hint_string
- r_hint_string = itos(elem_variant_type) + "/" + itos(elem_hint) + ":" + elem_hint_string;
r_hint = PROPERTY_HINT_TYPE_STRING;
} else if (p_allow_generics && p_variant_type == Variant::DICTIONARY) {
@@ -2939,7 +2977,7 @@ bool CSharpScript::_set(const StringName &p_name, const Variant &p_value) {
}
void CSharpScript::_get_property_list(List<PropertyInfo> *p_properties) const {
- p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+ p_properties->push_back(PropertyInfo(Variant::STRING, CSharpLanguage::singleton->string_names._script_source, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
void CSharpScript::_bind_methods() {
@@ -3034,13 +3072,13 @@ 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()) {
- MultiplayerAPI::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- MultiplayerAPI::RPCConfig nd;
+ 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 = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
+ 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);
@@ -3054,7 +3092,7 @@ void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
}
// Sort so we are 100% that they are always the same.
- p_script->rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
+ p_script->rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
p_script->load_script_signals(p_script->script_class, p_script->native);
}
@@ -3228,8 +3266,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
"Script inherits from native type '" + String(native_name) +
"', so it can't be instantiated in object of type: '" + p_this->get_class() + "'");
}
- ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) +
- "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(native_name) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'.");
}
}
@@ -3280,10 +3317,19 @@ void CSharpScript::get_script_method_list(List<MethodInfo> *p_list) const {
GD_MONO_SCOPE_THREAD_ATTACH;
- // TODO: Filter out things unsuitable for explicit calls, like constructors.
- const Vector<GDMonoMethod *> &methods = script_class->get_all_methods();
- for (int i = 0; i < methods.size(); ++i) {
- p_list->push_back(methods[i]->get_method_info());
+ // TODO: We're filtering out constructors but there may be other methods unsuitable for explicit calls.
+ GDMonoClass *top = script_class;
+
+ while (top && top != native) {
+ const Vector<GDMonoMethod *> &methods = top->get_all_methods();
+ for (int i = 0; i < methods.size(); ++i) {
+ MethodInfo minfo = methods[i]->get_method_info();
+ if (minfo.name != CACHED_STRING_NAME(dotctor)) {
+ p_list->push_back(methods[i]->get_method_info());
+ }
+ }
+
+ top = top->get_parent_class();
}
}
@@ -3453,9 +3499,15 @@ Ref<Script> CSharpScript::get_base_script() const {
return Ref<Script>();
}
-void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const {
- for (const KeyValue<StringName, PropertyInfo> &E : member_info) {
- p_list->push_back(E.value);
+void CSharpScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ List<PropertyInfo> props;
+
+ for (OrderedHashMap<StringName, PropertyInfo>::ConstElement E = member_info.front(); E; E = E.next()) {
+ props.push_front(E.value());
+ }
+
+ for (const PropertyInfo &prop : props) {
+ r_list->push_back(prop);
}
}
@@ -3464,18 +3516,18 @@ int CSharpScript::get_member_line(const StringName &p_member) const {
return -1;
}
-MultiplayerAPI::RPCMode CSharpScript::_member_get_rpc_mode(IMonoClassMember *p_member) const {
- if (p_member->has_attribute(CACHED_CLASS(RemoteAttribute))) {
- return MultiplayerAPI::RPC_MODE_ANY;
+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(PuppetAttribute))) {
- return MultiplayerAPI::RPC_MODE_AUTHORITY;
+ if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) {
+ return Multiplayer::RPC_MODE_AUTHORITY;
}
- return MultiplayerAPI::RPC_MODE_DISABLED;
+ return Multiplayer::RPC_MODE_DISABLED;
}
-const Vector<MultiplayerAPI::RPCConfig> CSharpScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const {
return rpc_functions;
}
@@ -3483,10 +3535,10 @@ Error CSharpScript::load_source_code(const String &p_path) {
Error ferr = read_all_file_utf8(p_path, source);
ERR_FAIL_COND_V_MSG(ferr != OK, ferr,
- ferr == 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." :
- "Failed to read file: '" + p_path + "'.");
+ ferr == 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."
+ : "Failed to read file: '" + p_path + "'.");
#ifdef TOOLS_ENABLED
source_changed_cache = true;
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 4552f376d0..c998d9c1e4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -136,7 +136,7 @@ private:
Map<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
- Vector<MultiplayerAPI::RPCConfig> rpc_functions;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
@@ -154,7 +154,7 @@ private:
Set<StringName> exported_members_names;
#endif
- Map<StringName, PropertyInfo> member_info;
+ OrderedHashMap<StringName, PropertyInfo> member_info;
void _clear();
@@ -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);
- MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
+ Multiplayer::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
protected:
static void _bind_methods();
@@ -215,7 +215,7 @@ public:
void get_script_signal_list(List<MethodInfo> *r_signals) const override;
bool get_property_default_value(const StringName &p_property, Variant &r_value) const override;
- void get_script_property_list(List<PropertyInfo> *p_list) const override;
+ void get_script_property_list(List<PropertyInfo> *r_list) const override;
void update_exports() override;
void get_members(Set<StringName> *p_members) override;
@@ -234,7 +234,7 @@ public:
int get_member_line(const StringName &p_member) const override;
- const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
bool is_placeholder_fallback_enabled() const override { return placeholder_fallback_enabled; }
@@ -293,7 +293,7 @@ public:
void get_property_list(List<PropertyInfo> *p_properties) const override;
Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const override;
- /* TODO */ void get_method_list(List<MethodInfo> *p_list) const override {}
+ void get_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
@@ -311,7 +311,7 @@ public:
void refcount_incremented() override;
bool refcount_decremented() override;
- const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
void notification(int p_notification) override;
void _call_notification(int p_notification);
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index 45a6f991bf..14c62b4bb0 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -8,17 +8,14 @@
See also [GodotSharp].
</description>
<tutorials>
- <link title="C# tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/c_sharp/index.html</link>
+ <link title="C# documentation index">$DOCS_URL/tutorials/scripting/c_sharp/index.html</link>
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <return type="Variant" />
<description>
Returns a new instance of the script.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 417f8ac704..a148072245 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -11,66 +11,55 @@
</tutorials>
<methods>
<method name="attach_thread">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Attaches the current thread to the Mono runtime.
</description>
</method>
<method name="detach_thread">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Detaches the current thread from the Mono runtime.
</description>
</method>
<method name="get_domain_id">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the current MonoDomain ID.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="get_scripts_domain_id">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the scripts MonoDomain's ID. This will be the same MonoDomain ID as [method get_domain_id], unless the scripts domain isn't loaded.
[b]Note:[/b] The Mono runtime must be initialized for this method to work (use [method is_runtime_initialized] to check). If the Mono runtime isn't initialized at the time this method is called, the engine will crash.
</description>
</method>
<method name="is_domain_finalizing_for_unload">
- <return type="bool">
- </return>
- <argument index="0" name="domain_id" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="domain_id" type="int" />
<description>
Returns [code]true[/code] if the domain is being finalized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_initialized">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the Mono runtime is initialized, [code]false[/code] otherwise.
</description>
</method>
<method name="is_runtime_shutting_down">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the Mono runtime is shutting down, [code]false[/code] otherwise.
</description>
</method>
<method name="is_scripts_domain_loaded">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the scripts domain is loaded, [code]false[/code] otherwise.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
index 92e299d2f3..397ede9644 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets
@@ -17,6 +17,6 @@
<!-- C# source generators -->
<ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
- <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
+ <PackageReference Include="Godot.SourceGenerators" Version="$(PackageFloatingVersion_Godot)" />
</ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
new file mode 100644
index 0000000000..2ddb8880c2
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Generic.cs
@@ -0,0 +1,16 @@
+namespace Godot.SourceGenerators.Sample
+{
+ partial class Generic<T> : Godot.Object
+ {
+ }
+
+ // Generic again but different generic parameters
+ partial class Generic<T, R> : Godot.Object
+ {
+ }
+
+ // Generic again but without generic parameters
+ partial class Generic : Godot.Object
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
index a51728e221..fa65595290 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -97,9 +97,13 @@ namespace Godot.SourceGenerators
string.Empty;
bool hasNamespace = classNs.Length != 0;
- string uniqueName = hasNamespace ?
- classNs + "." + className + "_ScriptPath_Generated" :
- className + "_ScriptPath_Generated";
+ var uniqueName = new StringBuilder();
+ if (hasNamespace)
+ uniqueName.Append($"{classNs}.");
+ uniqueName.Append(className);
+ if (symbol.IsGenericType)
+ uniqueName.Append($"Of{string.Join(string.Empty, symbol.TypeParameters)}");
+ uniqueName.Append("_ScriptPath_Generated");
var source = new StringBuilder();
@@ -121,6 +125,8 @@ namespace Godot.SourceGenerators
source.Append(attributes);
source.Append("\n partial class ");
source.Append(className);
+ if (symbol.IsGenericType)
+ source.Append($"<{string.Join(", ", symbol.TypeParameters)}>");
source.Append("\n{\n}\n");
if (hasNamespace)
@@ -128,7 +134,7 @@ namespace Godot.SourceGenerators
source.Append("\n}\n");
}
- context.AddSource(uniqueName, SourceText.From(source.ToString(), Encoding.UTF8));
+ context.AddSource(uniqueName.ToString(), SourceText.From(source.ToString(), Encoding.UTF8));
}
private static void AddScriptTypesAssemblyAttr(GeneratorExecutionContext context,
@@ -145,12 +151,15 @@ namespace Godot.SourceGenerators
foreach (var godotClass in godotClasses)
{
var qualifiedName = godotClass.Key.ToDisplayString(
- NullableFlowState.NotNull, SymbolDisplayFormat.FullyQualifiedFormat);
+ NullableFlowState.NotNull, SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGenericsOptions(SymbolDisplayGenericsOptions.None));
if (!first)
sourceBuilder.Append(", ");
first = false;
sourceBuilder.Append("typeof(");
sourceBuilder.Append(qualifiedName);
+ if (godotClass.Key.IsGenericType)
+ sourceBuilder.Append($"<{new string(',', godotClass.Key.TypeParameters.Count() - 1)}>");
sourceBuilder.Append(")");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 9b7b422276..2bf1cb7a18 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -12,12 +12,16 @@ namespace GodotTools.BuildLogger
public string Parameters { get; set; }
public LoggerVerbosity Verbosity { get; set; }
+ private StreamWriter _logStreamWriter;
+ private StreamWriter _issuesStreamWriter;
+ private int _indent;
+
public void Initialize(IEventSource eventSource)
{
if (null == Parameters)
throw new LoggerException("Log directory parameter not specified.");
- var parameters = Parameters.Split(new[] { ';' });
+ string[] parameters = Parameters.Split(new[] { ';' });
string logDir = parameters[0];
@@ -35,8 +39,8 @@ namespace GodotTools.BuildLogger
if (!Directory.Exists(logDir))
Directory.CreateDirectory(logDir);
- logStreamWriter = new StreamWriter(logFile);
- issuesStreamWriter = new StreamWriter(issuesFile);
+ _logStreamWriter = new StreamWriter(logFile);
+ _issuesStreamWriter = new StreamWriter(issuesFile);
}
catch (Exception ex)
{
@@ -66,12 +70,12 @@ namespace GodotTools.BuildLogger
private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
{
WriteLine(e.Message);
- indent++;
+ _indent++;
}
private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
{
- indent--;
+ _indent--;
WriteLine(e.Message);
}
@@ -87,7 +91,7 @@ namespace GodotTools.BuildLogger
string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
$"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
$"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
- issuesStreamWriter.WriteLine(errorLine);
+ _issuesStreamWriter.WriteLine(errorLine);
}
private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
@@ -102,7 +106,7 @@ namespace GodotTools.BuildLogger
string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
$"{e.Code?.CsvEscape() ?? string.Empty},{e.Message.CsvEscape()}," +
$"{e.ProjectFile?.CsvEscape() ?? string.Empty}";
- issuesStreamWriter.WriteLine(warningLine);
+ _issuesStreamWriter.WriteLine(warningLine);
}
private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
@@ -136,28 +140,24 @@ namespace GodotTools.BuildLogger
private void WriteLine(string line)
{
- for (int i = indent; i > 0; i--)
+ for (int i = _indent; i > 0; i--)
{
- logStreamWriter.Write("\t");
+ _logStreamWriter.Write("\t");
}
- logStreamWriter.WriteLine(line);
+ _logStreamWriter.WriteLine(line);
}
public void Shutdown()
{
- logStreamWriter.Close();
- issuesStreamWriter.Close();
+ _logStreamWriter.Close();
+ _issuesStreamWriter.Close();
}
private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
{
return Verbosity >= checkVerbosity;
}
-
- private StreamWriter logStreamWriter;
- private StreamWriter issuesStreamWriter;
- private int indent;
}
internal static class StringExtensions
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs
index 43d40f2ad9..a4d7dedbd5 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs
@@ -7,7 +7,7 @@ namespace GodotTools.Core
{
public static class ProcessExtensions
{
- public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken))
+ public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default)
{
var tcs = new TaskCompletionSource<bool>();
diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
index b217ae4bf7..60a4f297c9 100644
--- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
@@ -7,6 +7,8 @@ namespace GodotTools.Core
{
public static class StringExtensions
{
+ private static readonly string _driveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
+
public static string RelativeToPath(this string path, string dir)
{
// Make sure the directory ends with a path separator
@@ -49,13 +51,11 @@ namespace GodotTools.Core
return Path.DirectorySeparatorChar + path;
}
- private static readonly string DriveRoot = Path.GetPathRoot(Environment.CurrentDirectory);
-
public static bool IsAbsolutePath(this string path)
{
return path.StartsWith("/", StringComparison.Ordinal) ||
path.StartsWith("\\", StringComparison.Ordinal) ||
- path.StartsWith(DriveRoot, StringComparison.Ordinal);
+ path.StartsWith(_driveRoot, StringComparison.Ordinal);
}
public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false)
diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
index 0f50c90531..bc09e1ebf9 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs
@@ -122,13 +122,19 @@ namespace GodotTools.IdeMessaging
this.logger = logger;
string projectMetadataDir = Path.Combine(godotProjectDir, ".godot", "mono", "metadata");
+ // FileSystemWatcher requires an existing directory
+ if (!Directory.Exists(projectMetadataDir)) {
+ // Check if the non hidden version exists
+ string nonHiddenProjectMetadataDir = Path.Combine(godotProjectDir, "godot", "mono", "metadata");
+ if (Directory.Exists(nonHiddenProjectMetadataDir)) {
+ projectMetadataDir = nonHiddenProjectMetadataDir;
+ } else {
+ Directory.CreateDirectory(projectMetadataDir);
+ }
+ }
MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
- // FileSystemWatcher requires an existing directory
- if (!Directory.Exists(projectMetadataDir))
- Directory.CreateDirectory(projectMetadataDir);
-
fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
index 284e94810a..355b21d63a 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
@@ -9,15 +9,40 @@ namespace GodotTools.ProjectEditor
{
public class DotNetSolution
{
- private string directoryPath;
- private readonly Dictionary<string, ProjectInfo> projects = new Dictionary<string, ProjectInfo>();
+ private const string _solutionTemplate =
+@"Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+{0}
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+{1}
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+{2}
+ EndGlobalSection
+EndGlobal
+";
+
+ private const string _projectDeclaration =
+@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}""
+EndProject";
+
+ private const string _solutionPlatformsConfig =
+@" {0}|Any CPU = {0}|Any CPU";
+
+ private const string _projectPlatformsConfig =
+@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
+ {{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
+
+ private string _directoryPath;
+ private readonly Dictionary<string, ProjectInfo> _projects = new Dictionary<string, ProjectInfo>();
public string Name { get; }
public string DirectoryPath
{
- get => directoryPath;
- set => directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value);
+ get => _directoryPath;
+ set => _directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value);
}
public class ProjectInfo
@@ -29,22 +54,22 @@ namespace GodotTools.ProjectEditor
public void AddNewProject(string name, ProjectInfo projectInfo)
{
- projects[name] = projectInfo;
+ _projects[name] = projectInfo;
}
public bool HasProject(string name)
{
- return projects.ContainsKey(name);
+ return _projects.ContainsKey(name);
}
public ProjectInfo GetProjectInfo(string name)
{
- return projects[name];
+ return _projects[name];
}
public bool RemoveProject(string name)
{
- return projects.Remove(name);
+ return _projects.Remove(name);
}
public void Save()
@@ -58,7 +83,7 @@ namespace GodotTools.ProjectEditor
bool isFirstProject = true;
- foreach (var pair in projects)
+ foreach (var pair in _projects)
{
string name = pair.Key;
ProjectInfo projectInfo = pair.Value;
@@ -66,7 +91,7 @@ namespace GodotTools.ProjectEditor
if (!isFirstProject)
projectsDecl += "\n";
- projectsDecl += string.Format(ProjectDeclaration,
+ projectsDecl += string.Format(_projectDeclaration,
name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid);
for (int i = 0; i < projectInfo.Configs.Count; i++)
@@ -79,15 +104,15 @@ namespace GodotTools.ProjectEditor
projPlatformsCfg += "\n";
}
- slnPlatformsCfg += string.Format(SolutionPlatformsConfig, config);
- projPlatformsCfg += string.Format(ProjectPlatformsConfig, projectInfo.Guid, config);
+ slnPlatformsCfg += string.Format(_solutionPlatformsConfig, config);
+ projPlatformsCfg += string.Format(_projectPlatformsConfig, projectInfo.Guid, config);
}
isFirstProject = false;
}
string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
- string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
+ string content = string.Format(_solutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
File.WriteAllText(solutionPath, content, Encoding.UTF8); // UTF-8 with BOM
}
@@ -97,37 +122,12 @@ namespace GodotTools.ProjectEditor
Name = name;
}
- const string SolutionTemplate =
-@"Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-{0}
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
-{1}
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
-{2}
- EndGlobalSection
-EndGlobal
-";
-
- const string ProjectDeclaration =
-@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}""
-EndProject";
-
- const string SolutionPlatformsConfig =
-@" {0}|Any CPU = {0}|Any CPU";
-
- const string ProjectPlatformsConfig =
-@" {{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
- {{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
-
public static void MigrateFromOldConfigNames(string slnPath)
{
if (!File.Exists(slnPath))
return;
- var input = File.ReadAllText(slnPath);
+ string input = File.ReadAllText(slnPath);
if (!Regex.IsMatch(input, Regex.Escape("Tools|Any CPU")))
return;
@@ -151,7 +151,7 @@ EndProject";
};
var regex = new Regex(string.Join("|", dict.Keys.Select(Regex.Escape)));
- var result = regex.Replace(input, m => dict[m.Value]);
+ string result = regex.Replace(input, m => dict[m.Value]);
if (result != input)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
index ed77076df3..31363df9ef 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
@@ -91,7 +91,7 @@ namespace GodotTools.ProjectEditor
return identifier;
}
- static bool IsKeyword(string value, bool anyDoubleUnderscore)
+ private static bool IsKeyword(string value, bool anyDoubleUnderscore)
{
// Identifiers that start with double underscore are meant to be used for reserved keywords.
// Only existing keywords are enforced, but it may be useful to forbid any identifier
@@ -103,14 +103,14 @@ namespace GodotTools.ProjectEditor
}
else
{
- if (DoubleUnderscoreKeywords.Contains(value))
+ if (_doubleUnderscoreKeywords.Contains(value))
return true;
}
- return Keywords.Contains(value);
+ return _keywords.Contains(value);
}
- private static readonly HashSet<string> DoubleUnderscoreKeywords = new HashSet<string>
+ private static readonly HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
{
"__arglist",
"__makeref",
@@ -118,7 +118,7 @@ namespace GodotTools.ProjectEditor
"__refvalue",
};
- private static readonly HashSet<string> Keywords = new HashSet<string>
+ private static readonly HashSet<string> _keywords = new HashSet<string>
{
"as",
"do",
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index 27737c3da0..28bf57dc21 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -13,7 +13,8 @@ namespace GodotTools.Build
public string[] Targets { get; }
public string Configuration { get; }
public bool Restore { get; }
- public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
+ // TODO Use List once we have proper serialization
+ public Array<string> CustomProperties { get; } = new Array<string>();
public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
@@ -32,12 +33,12 @@ namespace GodotTools.Build
unchecked
{
int hash = 17;
- hash = hash * 29 + Solution.GetHashCode();
- hash = hash * 29 + Targets.GetHashCode();
- hash = hash * 29 + Configuration.GetHashCode();
- hash = hash * 29 + Restore.GetHashCode();
- hash = hash * 29 + CustomProperties.GetHashCode();
- hash = hash * 29 + LogsDirPath.GetHashCode();
+ hash = (hash * 29) + Solution.GetHashCode();
+ hash = (hash * 29) + Targets.GetHashCode();
+ hash = (hash * 29) + Configuration.GetHashCode();
+ hash = (hash * 29) + Restore.GetHashCode();
+ hash = (hash * 29) + CustomProperties.GetHashCode();
+ hash = (hash * 29) + LogsDirPath.GetHashCode();
return hash;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 2b6f972529..21bff70b15 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -32,7 +32,7 @@ namespace GodotTools.Build
private static void RemoveOldIssuesFile(BuildInfo buildInfo)
{
- var issuesFile = GetIssuesFilePath(buildInfo);
+ string issuesFile = GetIssuesFilePath(buildInfo);
if (!File.Exists(issuesFile))
return;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
index 25e260beed..56fca6b5cb 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -22,14 +22,6 @@ namespace GodotTools.Build
public string ProjectFile { get; set; }
}
- private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization
- private ItemList issuesList;
- private TextEdit buildLog;
- private PopupMenu issuesListContextMenu;
-
- private readonly object pendingBuildLogTextLock = new object();
- [NotNull] private string pendingBuildLogText = string.Empty;
-
[Signal] public event Action BuildStateChanged;
public bool HasBuildExited { get; private set; } = false;
@@ -60,13 +52,21 @@ namespace GodotTools.Build
}
}
- private BuildInfo BuildInfo { get; set; }
-
public bool LogVisible
{
- set => buildLog.Visible = value;
+ set => _buildLog.Visible = value;
}
+ // TODO Use List once we have proper serialization.
+ private readonly Array<BuildIssue> _issues = new Array<BuildIssue>();
+ private ItemList _issuesList;
+ private PopupMenu _issuesListContextMenu;
+ private TextEdit _buildLog;
+ private BuildInfo _buildInfo;
+
+ private readonly object _pendingBuildLogTextLock = new object();
+ [NotNull] private string _pendingBuildLogText = string.Empty;
+
private void LoadIssuesFromFile(string csvFile)
{
using (var file = new Godot.File())
@@ -107,7 +107,7 @@ namespace GodotTools.Build
else
ErrorCount += 1;
- issues.Add(issue);
+ _issues.Add(issue);
}
}
finally
@@ -119,21 +119,21 @@ namespace GodotTools.Build
private void IssueActivated(int idx)
{
- if (idx < 0 || idx >= issuesList.GetItemCount())
+ if (idx < 0 || idx >= _issuesList.ItemCount)
throw new IndexOutOfRangeException("Item list index out of range");
// Get correct issue idx from issue list
- int issueIndex = (int)(long)issuesList.GetItemMetadata(idx);
+ int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
- if (issueIndex < 0 || issueIndex >= issues.Count)
+ if (issueIndex < 0 || issueIndex >= _issues.Count)
throw new IndexOutOfRangeException("Issue index out of range");
- BuildIssue issue = issues[issueIndex];
+ BuildIssue issue = _issues[issueIndex];
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
return;
- string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir();
+ string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : _buildInfo.Solution.GetBaseDir();
string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
@@ -153,14 +153,14 @@ namespace GodotTools.Build
public void UpdateIssuesList()
{
- issuesList.Clear();
+ _issuesList.Clear();
using (var warningIcon = GetThemeIcon("Warning", "EditorIcons"))
using (var errorIcon = GetThemeIcon("Error", "EditorIcons"))
{
- for (int i = 0; i < issues.Count; i++)
+ for (int i = 0; i < _issues.Count; i++)
{
- BuildIssue issue = issues[i];
+ BuildIssue issue = _issues[i];
if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
continue;
@@ -191,11 +191,11 @@ namespace GodotTools.Build
int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
- issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
+ _issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
- int index = issuesList.GetItemCount() - 1;
- issuesList.SetItemTooltip(index, tooltip);
- issuesList.SetItemMetadata(index, i);
+ int index = _issuesList.ItemCount - 1;
+ _issuesList.SetItemTooltip(index, tooltip);
+ _issuesList.SetItemMetadata(index, i);
}
}
}
@@ -205,12 +205,12 @@ namespace GodotTools.Build
HasBuildExited = true;
BuildResult = Build.BuildResult.Error;
- issuesList.Clear();
+ _issuesList.Clear();
- var issue = new BuildIssue {Message = cause, Warning = false};
+ var issue = new BuildIssue { Message = cause, Warning = false };
ErrorCount += 1;
- issues.Add(issue);
+ _issues.Add(issue);
UpdateIssuesList();
@@ -219,13 +219,13 @@ namespace GodotTools.Build
private void BuildStarted(BuildInfo buildInfo)
{
- BuildInfo = buildInfo;
+ _buildInfo = buildInfo;
HasBuildExited = false;
- issues.Clear();
+ _issues.Clear();
WarningCount = 0;
ErrorCount = 0;
- buildLog.Text = string.Empty;
+ _buildLog.Text = string.Empty;
UpdateIssuesList();
@@ -237,7 +237,7 @@ namespace GodotTools.Build
HasBuildExited = true;
BuildResult = result;
- LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+ LoadIssuesFromFile(Path.Combine(_buildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
UpdateIssuesList();
@@ -246,46 +246,46 @@ namespace GodotTools.Build
private void UpdateBuildLogText()
{
- lock (pendingBuildLogTextLock)
+ lock (_pendingBuildLogTextLock)
{
- buildLog.Text += pendingBuildLogText;
- pendingBuildLogText = string.Empty;
+ _buildLog.Text += _pendingBuildLogText;
+ _pendingBuildLogText = string.Empty;
ScrollToLastNonEmptyLogLine();
}
}
private void StdOutputReceived(string text)
{
- lock (pendingBuildLogTextLock)
+ lock (_pendingBuildLogTextLock)
{
- if (pendingBuildLogText.Length == 0)
+ if (_pendingBuildLogText.Length == 0)
CallDeferred(nameof(UpdateBuildLogText));
- pendingBuildLogText += text + "\n";
+ _pendingBuildLogText += text + "\n";
}
}
private void StdErrorReceived(string text)
{
- lock (pendingBuildLogTextLock)
+ lock (_pendingBuildLogTextLock)
{
- if (pendingBuildLogText.Length == 0)
+ if (_pendingBuildLogText.Length == 0)
CallDeferred(nameof(UpdateBuildLogText));
- pendingBuildLogText += text + "\n";
+ _pendingBuildLogText += text + "\n";
}
}
private void ScrollToLastNonEmptyLogLine()
{
int line;
- for (line = buildLog.GetLineCount(); line > 0; line--)
+ for (line = _buildLog.GetLineCount(); line > 0; line--)
{
- string lineText = buildLog.GetLine(line);
+ string lineText = _buildLog.GetLine(line);
if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim()))
break;
}
- buildLog.SetCaretLine(line);
+ _buildLog.SetCaretLine(line);
}
public void RestartBuild()
@@ -318,11 +318,11 @@ namespace GodotTools.Build
// We don't allow multi-selection but just in case that changes later...
string text = null;
- foreach (int issueIndex in issuesList.GetSelectedItems())
+ foreach (int issueIndex in _issuesList.GetSelectedItems())
{
if (text != null)
text += "\n";
- text += issuesList.GetItemText(issueIndex);
+ text += _issuesList.GetItemText(issueIndex);
}
if (text != null)
@@ -338,20 +338,20 @@ namespace GodotTools.Build
{
_ = index; // Unused
- issuesListContextMenu.Clear();
- issuesListContextMenu.Size = new Vector2i(1, 1);
+ _issuesListContextMenu.Clear();
+ _issuesListContextMenu.Size = new Vector2i(1, 1);
- if (issuesList.IsAnythingSelected())
+ if (_issuesList.IsAnythingSelected())
{
// Add menu entries for the selected item
- issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
+ _issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy);
}
- if (issuesListContextMenu.GetItemCount() > 0)
+ if (_issuesListContextMenu.ItemCount > 0)
{
- issuesListContextMenu.Position = (Vector2i)(issuesList.RectGlobalPosition + atPosition);
- issuesListContextMenu.Popup();
+ _issuesListContextMenu.Position = (Vector2i)(_issuesList.RectGlobalPosition + atPosition);
+ _issuesListContextMenu.Popup();
}
}
@@ -368,27 +368,27 @@ namespace GodotTools.Build
};
AddChild(hsc);
- issuesList = new ItemList
+ _issuesList = new ItemList
{
SizeFlagsVertical = (int)SizeFlags.ExpandFill,
SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the build log
};
- issuesList.ItemActivated += IssueActivated;
- issuesList.AllowRmbSelect = true;
- issuesList.ItemRmbSelected += IssuesListRmbSelected;
- hsc.AddChild(issuesList);
+ _issuesList.ItemActivated += IssueActivated;
+ _issuesList.AllowRmbSelect = true;
+ _issuesList.ItemRmbSelected += IssuesListRmbSelected;
+ hsc.AddChild(_issuesList);
- issuesListContextMenu = new PopupMenu();
- issuesListContextMenu.IdPressed += IssuesListContextOptionPressed;
- issuesList.AddChild(issuesListContextMenu);
+ _issuesListContextMenu = new PopupMenu();
+ _issuesListContextMenu.IdPressed += IssuesListContextOptionPressed;
+ _issuesList.AddChild(_issuesListContextMenu);
- buildLog = new TextEdit
+ _buildLog = new TextEdit
{
Editable = false,
SizeFlagsVertical = (int)SizeFlags.ExpandFill,
SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
};
- hsc.AddChild(buildLog);
+ hsc.AddChild(_buildLog);
AddBuildEventListeners();
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 5f35d506de..e9cf7911be 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -11,10 +11,10 @@ namespace GodotTools.Build
{
public BuildOutputView BuildOutputView { get; private set; }
- private MenuButton buildMenuBtn;
- private Button errorsBtn;
- private Button warningsBtn;
- private Button viewLogBtn;
+ private MenuButton _buildMenuBtn;
+ private Button _errorsBtn;
+ private Button _warningsBtn;
+ private Button _viewLogBtn;
private void WarningsToggled(bool pressed)
{
@@ -132,16 +132,16 @@ namespace GodotTools.Build
var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
AddChild(toolBarHBox);
- buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
- toolBarHBox.AddChild(buildMenuBtn);
+ _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
+ toolBarHBox.AddChild(_buildMenuBtn);
- var buildMenu = buildMenuBtn.GetPopup();
+ var buildMenu = _buildMenuBtn.GetPopup();
buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution);
buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution);
buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution);
buildMenu.IdPressed += BuildMenuOptionPressed;
- errorsBtn = new Button
+ _errorsBtn = new Button
{
HintTooltip = "Show Errors".TTR(),
Icon = GetThemeIcon("StatusError", "EditorIcons"),
@@ -150,10 +150,10 @@ namespace GodotTools.Build
Pressed = true,
FocusMode = FocusModeEnum.None
};
- errorsBtn.Toggled += ErrorsToggled;
- toolBarHBox.AddChild(errorsBtn);
+ _errorsBtn.Toggled += ErrorsToggled;
+ toolBarHBox.AddChild(_errorsBtn);
- warningsBtn = new Button
+ _warningsBtn = new Button
{
HintTooltip = "Show Warnings".TTR(),
Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
@@ -162,18 +162,18 @@ namespace GodotTools.Build
Pressed = true,
FocusMode = FocusModeEnum.None
};
- warningsBtn.Toggled += WarningsToggled;
- toolBarHBox.AddChild(warningsBtn);
+ _warningsBtn.Toggled += WarningsToggled;
+ toolBarHBox.AddChild(_warningsBtn);
- viewLogBtn = new Button
+ _viewLogBtn = new Button
{
Text = "Show Output".TTR(),
ToggleMode = true,
Pressed = true,
FocusMode = FocusModeEnum.None
};
- viewLogBtn.Toggled += ViewLogToggled;
- toolBarHBox.AddChild(viewLogBtn);
+ _viewLogBtn.Toggled += ViewLogToggled;
+ toolBarHBox.AddChild(_viewLogBtn);
BuildOutputView = new BuildOutputView();
AddChild(BuildOutputView);
@@ -185,12 +185,12 @@ namespace GodotTools.Build
if (what == NotificationThemeChanged)
{
- if (buildMenuBtn != null)
- buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons");
- if (errorsBtn != null)
- errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons");
- if (warningsBtn != null)
- warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons");
+ if (_buildMenuBtn != null)
+ _buildMenuBtn.Icon = GetThemeIcon("Play", "EditorIcons");
+ if (_errorsBtn != null)
+ _errorsBtn.Icon = GetThemeIcon("StatusError", "EditorIcons");
+ if (_warningsBtn != null)
+ _warningsBtn.Icon = GetThemeIcon("NodeWarning", "EditorIcons");
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index 774c49e705..a859c6f717 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -61,7 +61,7 @@ namespace GodotTools.Build
}
case BuildTool.JetBrainsMsBuild:
{
- var editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
+ string editorPath = (string)editorSettings.GetSetting(RiderPathManager.EditorPathSettingName);
if (!File.Exists(editorPath))
throw new FileNotFoundException($"Cannot find Rider executable. Tried with path: {editorPath}");
@@ -165,7 +165,9 @@ namespace GodotTools.Build
// Try to find 15.0 with vswhere
- var envNames = Internal.GodotIs32Bits() ? new[] {"ProgramFiles", "ProgramW6432"} : new[] {"ProgramFiles(x86)", "ProgramFiles"};
+ string[] envNames = Internal.GodotIs32Bits() ?
+ envNames = new[] { "ProgramFiles", "ProgramW6432" } :
+ envNames = new[] { "ProgramFiles(x86)", "ProgramFiles" };
string vsWherePath = null;
foreach (var envName in envNames)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index f69307104f..37e6a34977 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
@@ -65,12 +65,12 @@ namespace GodotTools.Export
if (platform == OS.Platforms.iOS)
{
- var architectures = GetEnablediOSArchs(features).ToArray();
+ string[] architectures = GetEnablediOSArchs(features).ToArray();
CompileAssembliesForiOS(exporter, isDebug, architectures, aotOpts, aotTempDir, assembliesPrepared, bclDir);
}
else if (platform == OS.Platforms.Android)
{
- var abis = GetEnabledAndroidAbis(features).ToArray();
+ string[] abis = GetEnabledAndroidAbis(features).ToArray();
CompileAssembliesForAndroid(exporter, isDebug, abis, aotOpts, aotTempDir, assembliesPrepared, bclDir);
}
else
@@ -138,7 +138,8 @@ namespace GodotTools.Export
}
else
{
- string outputDataLibDir = Path.Combine(outputDataDir, "Mono", platform == OS.Platforms.Windows ? "bin" : "lib");
+ string libDir = platform == OS.Platforms.Windows ? "bin" : "lib";
+ string outputDataLibDir = Path.Combine(outputDataDir, "Mono", libDir);
File.Copy(tempOutputFilePath, Path.Combine(outputDataLibDir, outputFileName));
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 0b5aa72a81..3e46a89b7c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -20,7 +20,7 @@ namespace GodotTools.Export
public class ExportPlugin : EditorExportPlugin
{
[Flags]
- enum I18NCodesets : long
+ private enum I18NCodesets : long
{
None = 0,
CJK = 1,
@@ -31,6 +31,8 @@ namespace GodotTools.Export
All = CJK | MidEast | Other | Rare | West
}
+ private string _maybeLastExportError;
+
private void AddI18NAssemblies(Godot.Collections.Dictionary<string, string> assemblies, string bclDir)
{
var codesets = (I18NCodesets)ProjectSettings.GetSetting("mono/export/i18n_codesets");
@@ -83,8 +85,6 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/android_toolchain_path", "");
}
- private string maybeLastExportError;
-
private void AddFile(string srcPath, string dstPath, bool remap = false)
{
// Add file to the PCK
@@ -129,14 +129,14 @@ namespace GodotTools.Export
}
catch (Exception e)
{
- maybeLastExportError = e.Message;
+ _maybeLastExportError = e.Message;
// 'maybeLastExportError' cannot be null or empty if there was an error, so we
// must consider the possibility of exceptions being thrown without a message.
- if (string.IsNullOrEmpty(maybeLastExportError))
- maybeLastExportError = $"Exception thrown: {e.GetType().Name}";
+ if (string.IsNullOrEmpty(_maybeLastExportError))
+ _maybeLastExportError = $"Exception thrown: {e.GetType().Name}";
- GD.PushError($"Failed to export project: {maybeLastExportError}");
+ GD.PushError($"Failed to export project: {_maybeLastExportError}");
Console.Error.WriteLine(e);
// TODO: Do something on error once _ExportBegin supports failing.
}
@@ -317,10 +317,10 @@ namespace GodotTools.Export
Directory.Delete(aotTempDir, recursive: true);
// TODO: Just a workaround until the export plugins can be made to abort with errors
- if (!string.IsNullOrEmpty(maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
+ if (!string.IsNullOrEmpty(_maybeLastExportError)) // Check empty as well, because it's set to empty after hot-reloading
{
- string lastExportError = maybeLastExportError;
- maybeLastExportError = null;
+ string lastExportError = _maybeLastExportError;
+ _maybeLastExportError = null;
GodotSharpEditor.Instance.ShowErrorDialog(lastExportError, "Failed to export C# project");
}
@@ -470,7 +470,7 @@ namespace GodotTools.Export
private static string DetermineDataDirNameForProject()
{
- var appName = (string)ProjectSettings.GetSetting("application/config/name");
+ string appName = (string)ProjectSettings.GetSetting("application/config/name");
string appNameSafe = appName.ToSafeDirName();
return $"data_{appNameSafe}";
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 1faa6eeac0..98c6881166 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -21,18 +21,19 @@ namespace GodotTools
{
public class GodotSharpEditor : EditorPlugin, ISerializationListener
{
- private EditorSettings editorSettings;
+ private EditorSettings _editorSettings;
- private PopupMenu menuPopup;
+ private PopupMenu _menuPopup;
- private AcceptDialog errorDialog;
+ private AcceptDialog _errorDialog;
- private Button bottomPanelBtn;
- private Button toolBarBuildButton;
+ private Button _bottomPanelBtn;
+ private Button _toolBarBuildButton;
- public GodotIdeManager GodotIdeManager { get; private set; }
+ // TODO Use WeakReference once we have proper serialization.
+ private WeakRef _exportPluginWeak;
- private WeakRef exportPluginWeak; // TODO Use WeakReference once we have proper serialization
+ public GodotIdeManager GodotIdeManager { get; private set; }
public MSBuildPanel MSBuildPanel { get; private set; }
@@ -42,7 +43,7 @@ namespace GodotTools
{
get
{
- var projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
+ string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name");
projectAssemblyName = projectAssemblyName.ToSafeDirName();
if (string.IsNullOrEmpty(projectAssemblyName))
projectAssemblyName = "UnnamedProject";
@@ -123,9 +124,9 @@ namespace GodotTools
private void _RemoveCreateSlnMenuOption()
{
- menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
- bottomPanelBtn.Show();
- toolBarBuildButton.Show();
+ _menuPopup.RemoveItem(_menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
+ _bottomPanelBtn.Show();
+ _toolBarBuildButton.Show();
}
private void _MenuOptionPressed(int id)
@@ -181,9 +182,9 @@ namespace GodotTools
public void ShowErrorDialog(string message, string title = "Error")
{
- errorDialog.Title = title;
- errorDialog.DialogText = message;
- errorDialog.PopupCentered();
+ _errorDialog.Title = title;
+ _errorDialog.DialogText = message;
+ _errorDialog.PopupCentered();
}
private static string _vsCodePath = string.Empty;
@@ -196,7 +197,7 @@ namespace GodotTools
[UsedImplicitly]
public Error OpenInExternalEditor(Script script, int line, int col)
{
- var editorId = (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor");
+ var editorId = (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor");
switch (editorId)
{
@@ -287,7 +288,7 @@ namespace GodotTools
}
}
- var resourcePath = ProjectSettings.GlobalizePath("res://");
+ string resourcePath = ProjectSettings.GlobalizePath("res://");
args.Add(resourcePath);
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
@@ -346,7 +347,7 @@ namespace GodotTools
[UsedImplicitly]
public bool OverridesExternalEditor()
{
- return (ExternalEditorId)editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
+ return (ExternalEditorId)_editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditorId.None;
}
public override bool _Build()
@@ -387,8 +388,8 @@ namespace GodotTools
private void BuildStateChanged()
{
- if (bottomPanelBtn != null)
- bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
+ if (_bottomPanelBtn != null)
+ _bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
}
public override void _EnablePlugin()
@@ -402,29 +403,33 @@ namespace GodotTools
var editorInterface = GetEditorInterface();
var editorBaseControl = editorInterface.GetBaseControl();
- editorSettings = editorInterface.GetEditorSettings();
+ _editorSettings = editorInterface.GetEditorSettings();
- errorDialog = new AcceptDialog();
- editorBaseControl.AddChild(errorDialog);
+ _errorDialog = new AcceptDialog();
+ editorBaseControl.AddChild(_errorDialog);
MSBuildPanel = new MSBuildPanel();
- bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
+ _bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
- menuPopup = new PopupMenu();
- menuPopup.Hide();
+ _menuPopup = new PopupMenu();
+ _menuPopup.Hide();
+
+ AddToolSubmenuItem("C#", _menuPopup);
- AddToolSubmenuItem("C#", menuPopup);
+ var buildSolutionShortcut = (Shortcut)EditorShortcut("mono/build_solution");
- toolBarBuildButton = new Button
+ _toolBarBuildButton = new Button
{
Text = "Build",
- HintTooltip = "Build solution",
- FocusMode = Control.FocusModeEnum.None
+ HintTooltip = "Build Solution".TTR(),
+ FocusMode = Control.FocusModeEnum.None,
+ Shortcut = buildSolutionShortcut,
+ ShortcutInTooltip = true
};
- toolBarBuildButton.PressedSignal += BuildSolutionPressed;
- AddControlToContainer(CustomControlContainer.Toolbar, toolBarBuildButton);
+ _toolBarBuildButton.PressedSignal += BuildSolutionPressed;
+ AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
@@ -432,12 +437,12 @@ namespace GodotTools
}
else
{
- bottomPanelBtn.Hide();
- toolBarBuildButton.Hide();
- menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
+ _bottomPanelBtn.Hide();
+ _toolBarBuildButton.Hide();
+ _menuPopup.AddItem("Create C# solution".TTR(), (int)MenuOptions.CreateSln);
}
- menuPopup.IdPressed += _MenuOptionPressed;
+ _menuPopup.IdPressed += _MenuOptionPressed;
// External editor settings
EditorDef("mono/editor/external_editor", ExternalEditorId.None);
@@ -465,7 +470,7 @@ namespace GodotTools
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
- editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+ _editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Variant.Type.Int,
["name"] = "mono/editor/external_editor",
@@ -477,7 +482,7 @@ namespace GodotTools
var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
exportPlugin.RegisterExportSettings();
- exportPluginWeak = WeakRef(exportPlugin);
+ _exportPluginWeak = WeakRef(exportPlugin);
try
{
@@ -500,15 +505,15 @@ namespace GodotTools
{
base.Dispose(disposing);
- if (exportPluginWeak != null)
+ if (_exportPluginWeak != null)
{
// We need to dispose our export plugin before the editor destroys EditorSettings.
// Otherwise, if the GC disposes it at a later time, EditorExportPlatformAndroid
// will be freed after EditorSettings already was, and its device polling thread
// will try to access the EditorSettings singleton, resulting in null dereferencing.
- (exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
+ (_exportPluginWeak.GetRef() as ExportPlugin)?.Dispose();
- exportPluginWeak.Dispose();
+ _exportPluginWeak.Dispose();
}
GodotIdeManager?.Dispose();
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index b30c857c64..dd05f28af0 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -6,7 +6,7 @@ namespace GodotTools
{
public class HotReloadAssemblyWatcher : Node
{
- private Timer watchTimer;
+ private Timer _watchTimer;
public override void _Notification(int what)
{
@@ -27,22 +27,22 @@ namespace GodotTools
public void RestartTimer()
{
- watchTimer.Stop();
- watchTimer.Start();
+ _watchTimer.Stop();
+ _watchTimer.Start();
}
public override void _Ready()
{
base._Ready();
- watchTimer = new Timer
+ _watchTimer = new Timer
{
OneShot = false,
WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
};
- watchTimer.Timeout += TimerTimeout;
- AddChild(watchTimer);
- watchTimer.Start();
+ _watchTimer.Timeout += TimerTimeout;
+ AddChild(_watchTimer);
+ _watchTimer.Start();
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 451ce39f5c..23339fe50b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -10,22 +10,22 @@ namespace GodotTools.Ides
{
public sealed class GodotIdeManager : Node, ISerializationListener
{
- private MessagingServer MessagingServer { get; set; }
+ private MessagingServer _messagingServer;
- private MonoDevelop.Instance monoDevelInstance;
- private MonoDevelop.Instance vsForMacInstance;
+ private MonoDevelop.Instance _monoDevelInstance;
+ private MonoDevelop.Instance _vsForMacInstance;
private MessagingServer GetRunningOrNewServer()
{
- if (MessagingServer != null && !MessagingServer.IsDisposed)
- return MessagingServer;
+ if (_messagingServer != null && !_messagingServer.IsDisposed)
+ return _messagingServer;
- MessagingServer?.Dispose();
- MessagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger());
+ _messagingServer?.Dispose();
+ _messagingServer = new MessagingServer(OS.GetExecutablePath(), ProjectSettings.GlobalizePath(GodotSharpDirs.ResMetadataDir), new GodotLogger());
- _ = MessagingServer.Listen();
+ _ = _messagingServer.Listen();
- return MessagingServer;
+ return _messagingServer;
}
public override void _Ready()
@@ -48,7 +48,7 @@ namespace GodotTools.Ides
if (disposing)
{
- MessagingServer?.Dispose();
+ _messagingServer?.Dispose();
}
}
@@ -113,14 +113,14 @@ namespace GodotTools.Ides
{
if (Utils.OS.IsMacOS && editorId == ExternalEditorId.VisualStudioForMac)
{
- vsForMacInstance = (vsForMacInstance?.IsDisposed ?? true ? null : vsForMacInstance) ??
+ _vsForMacInstance = (_vsForMacInstance?.IsDisposed ?? true ? null : _vsForMacInstance) ??
new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.VisualStudioForMac);
- return vsForMacInstance;
+ return _vsForMacInstance;
}
- monoDevelInstance = (monoDevelInstance?.IsDisposed ?? true ? null : monoDevelInstance) ??
+ _monoDevelInstance = (_monoDevelInstance?.IsDisposed ?? true ? null : _monoDevelInstance) ??
new MonoDevelop.Instance(solutionPath, MonoDevelop.EditorId.MonoDevelop);
- return monoDevelInstance;
+ return _monoDevelInstance;
}
try
@@ -159,15 +159,15 @@ namespace GodotTools.Ides
public readonly struct EditorPick
{
- private readonly string identity;
+ private readonly string _identity;
public EditorPick(string identity)
{
- this.identity = identity;
+ _identity = identity;
}
public bool IsAnyConnected() =>
- GodotSharpEditor.Instance.GodotIdeManager.GetRunningOrNewServer().IsAnyConnected(identity);
+ GodotSharpEditor.Instance.GodotIdeManager.GetRunningOrNewServer().IsAnyConnected(_identity);
private void SendRequest<TResponse>(Request request)
where TResponse : Response, new()
@@ -175,7 +175,7 @@ namespace GodotTools.Ides
// Logs an error if no client is connected with the specified identity
GodotSharpEditor.Instance.GodotIdeManager
.GetRunningOrNewServer()
- .BroadcastRequest<TResponse>(identity, request);
+ .BroadcastRequest<TResponse>(_identity, request);
}
public void SendOpenFile(string file)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index eb34a2d0f7..6f11831b80 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -21,24 +21,26 @@ namespace GodotTools.Ides
{
public sealed class MessagingServer : IDisposable
{
- private readonly ILogger logger;
+ private readonly ILogger _logger;
- private readonly FileStream metaFile;
- private string MetaFilePath { get; }
+ private readonly FileStream _metaFile;
+ private string _metaFilePath;
- private readonly SemaphoreSlim peersSem = new SemaphoreSlim(1);
+ private readonly SemaphoreSlim _peersSem = new SemaphoreSlim(1);
- private readonly TcpListener listener;
+ private readonly TcpListener _listener;
- private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> clientConnectedAwaiters = new Dictionary<string, Queue<NotifyAwaiter<bool>>>();
- private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> clientDisconnectedAwaiters = new Dictionary<string, Queue<NotifyAwaiter<bool>>>();
+ private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> _clientConnectedAwaiters =
+ new Dictionary<string, Queue<NotifyAwaiter<bool>>>();
+ private readonly Dictionary<string, Queue<NotifyAwaiter<bool>>> _clientDisconnectedAwaiters =
+ new Dictionary<string, Queue<NotifyAwaiter<bool>>>();
public async Task<bool> AwaitClientConnected(string identity)
{
- if (!clientConnectedAwaiters.TryGetValue(identity, out var queue))
+ if (!_clientConnectedAwaiters.TryGetValue(identity, out var queue))
{
queue = new Queue<NotifyAwaiter<bool>>();
- clientConnectedAwaiters.Add(identity, queue);
+ _clientConnectedAwaiters.Add(identity, queue);
}
var awaiter = new NotifyAwaiter<bool>();
@@ -48,10 +50,10 @@ namespace GodotTools.Ides
public async Task<bool> AwaitClientDisconnected(string identity)
{
- if (!clientDisconnectedAwaiters.TryGetValue(identity, out var queue))
+ if (!_clientDisconnectedAwaiters.TryGetValue(identity, out var queue))
{
queue = new Queue<NotifyAwaiter<bool>>();
- clientDisconnectedAwaiters.Add(identity, queue);
+ _clientDisconnectedAwaiters.Add(identity, queue);
}
var awaiter = new NotifyAwaiter<bool>();
@@ -77,7 +79,7 @@ namespace GodotTools.Ides
if (IsDisposed)
return;
- using (await peersSem.UseAsync())
+ using (await _peersSem.UseAsync())
{
if (IsDisposed) // lock may not be fair
return;
@@ -95,19 +97,19 @@ namespace GodotTools.Ides
foreach (var connection in Peers)
connection.Dispose();
Peers.Clear();
- listener?.Stop();
+ _listener?.Stop();
- metaFile?.Dispose();
+ _metaFile?.Dispose();
- File.Delete(MetaFilePath);
+ File.Delete(_metaFilePath);
}
}
public MessagingServer(string editorExecutablePath, string projectMetadataDir, ILogger logger)
{
- this.logger = logger;
+ this._logger = logger;
- MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
+ _metaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);
// Make sure the directory exists
Directory.CreateDirectory(projectMetadataDir);
@@ -115,13 +117,13 @@ namespace GodotTools.Ides
// The Godot editor's file system thread can keep the file open for writing, so we are forced to allow write sharing...
const FileShare metaFileShare = FileShare.ReadWrite;
- metaFile = File.Open(MetaFilePath, FileMode.Create, FileAccess.Write, metaFileShare);
+ _metaFile = File.Open(_metaFilePath, FileMode.Create, FileAccess.Write, metaFileShare);
- listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, port: 0));
- listener.Start();
+ _listener = new TcpListener(new IPEndPoint(IPAddress.Loopback, port: 0));
+ _listener.Start();
- int port = ((IPEndPoint)listener.Server.LocalEndPoint).Port;
- using (var metaFileWriter = new StreamWriter(metaFile, Encoding.UTF8))
+ int port = ((IPEndPoint)_listener.Server.LocalEndPoint).Port;
+ using (var metaFileWriter = new StreamWriter(_metaFile, Encoding.UTF8))
{
metaFileWriter.WriteLine(port);
metaFileWriter.WriteLine(editorExecutablePath);
@@ -130,30 +132,30 @@ namespace GodotTools.Ides
private async Task AcceptClient(TcpClient tcpClient)
{
- logger.LogDebug("Accept client...");
+ _logger.LogDebug("Accept client...");
- using (var peer = new Peer(tcpClient, new ServerHandshake(), new ServerMessageHandler(), logger))
+ using (var peer = new Peer(tcpClient, new ServerHandshake(), new ServerMessageHandler(), _logger))
{
// ReSharper disable AccessToDisposedClosure
peer.Connected += () =>
{
- logger.LogInfo("Connection open with Ide Client");
+ _logger.LogInfo("Connection open with Ide Client");
- if (clientConnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue))
+ if (_clientConnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue))
{
while (queue.Count > 0)
queue.Dequeue().SetResult(true);
- clientConnectedAwaiters.Remove(peer.RemoteIdentity);
+ _clientConnectedAwaiters.Remove(peer.RemoteIdentity);
}
};
peer.Disconnected += () =>
{
- if (clientDisconnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue))
+ if (_clientDisconnectedAwaiters.TryGetValue(peer.RemoteIdentity, out var queue))
{
while (queue.Count > 0)
queue.Dequeue().SetResult(true);
- clientDisconnectedAwaiters.Remove(peer.RemoteIdentity);
+ _clientDisconnectedAwaiters.Remove(peer.RemoteIdentity);
}
};
// ReSharper restore AccessToDisposedClosure
@@ -162,17 +164,17 @@ namespace GodotTools.Ides
{
if (!await peer.DoHandshake("server"))
{
- logger.LogError("Handshake failed");
+ _logger.LogError("Handshake failed");
return;
}
}
catch (Exception e)
{
- logger.LogError("Handshake failed with unhandled exception: ", e);
+ _logger.LogError("Handshake failed with unhandled exception: ", e);
return;
}
- using (await peersSem.UseAsync())
+ using (await _peersSem.UseAsync())
Peers.Add(peer);
try
@@ -181,7 +183,7 @@ namespace GodotTools.Ides
}
finally
{
- using (await peersSem.UseAsync())
+ using (await _peersSem.UseAsync())
Peers.Remove(peer);
}
}
@@ -192,7 +194,7 @@ namespace GodotTools.Ides
try
{
while (!IsDisposed)
- _ = AcceptClient(await listener.AcceptTcpClientAsync());
+ _ = AcceptClient(await _listener.AcceptTcpClientAsync());
}
catch (Exception e)
{
@@ -204,11 +206,11 @@ namespace GodotTools.Ides
public async void BroadcastRequest<TResponse>(string identity, Request request)
where TResponse : Response, new()
{
- using (await peersSem.UseAsync())
+ using (await _peersSem.UseAsync())
{
if (!IsAnyConnected(identity))
{
- logger.LogError("Cannot write request. No client connected to the Godot Ide Server.");
+ _logger.LogError("Cannot write request. No client connected to the Godot Ide Server.");
return;
}
@@ -225,16 +227,19 @@ namespace GodotTools.Ides
private class ServerHandshake : IHandshake
{
- private static readonly string ServerHandshakeBase = $"{Peer.ServerHandshakeName},Version={Peer.ProtocolVersionMajor}.{Peer.ProtocolVersionMinor}.{Peer.ProtocolVersionRevision}";
- private static readonly string ClientHandshakePattern = $@"{Regex.Escape(Peer.ClientHandshakeName)},Version=([0-9]+)\.([0-9]+)\.([0-9]+),([_a-zA-Z][_a-zA-Z0-9]{{0,63}})";
+ private static readonly string _serverHandshakeBase =
+ $"{Peer.ServerHandshakeName},Version={Peer.ProtocolVersionMajor}.{Peer.ProtocolVersionMinor}.{Peer.ProtocolVersionRevision}";
- public string GetHandshakeLine(string identity) => $"{ServerHandshakeBase},{identity}";
+ private static readonly string _clientHandshakePattern =
+ $@"{Regex.Escape(Peer.ClientHandshakeName)},Version=([0-9]+)\.([0-9]+)\.([0-9]+),([_a-zA-Z][_a-zA-Z0-9]{{0,63}})";
+
+ public string GetHandshakeLine(string identity) => $"{_serverHandshakeBase},{identity}";
public bool IsValidPeerHandshake(string handshake, out string identity, ILogger logger)
{
identity = null;
- var match = Regex.Match(handshake, ClientHandshakePattern);
+ var match = Regex.Match(handshake, _clientHandshakePattern);
if (!match.Success)
return false;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
index fd7bbd5578..3f1d5ac3ca 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -10,17 +10,17 @@ namespace GodotTools.Ides.MonoDevelop
public class Instance : IDisposable
{
public DateTime LaunchTime { get; private set; }
- private readonly string solutionFile;
- private readonly EditorId editorId;
+ private readonly string _solutionFile;
+ private readonly EditorId _editorId;
- private Process process;
+ private Process _process;
- public bool IsRunning => process != null && !process.HasExited;
+ public bool IsRunning => _process != null && !_process.HasExited;
public bool IsDisposed { get; private set; }
public void Execute()
{
- bool newWindow = process == null || process.HasExited;
+ bool newWindow = _process == null || _process.HasExited;
var args = new List<string>();
@@ -28,7 +28,7 @@ namespace GodotTools.Ides.MonoDevelop
if (OS.IsMacOS)
{
- string bundleId = BundleIds[editorId];
+ string bundleId = BundleIds[_editorId];
if (Internal.IsOsxAppBundleInstalled(bundleId))
{
@@ -45,18 +45,18 @@ namespace GodotTools.Ides.MonoDevelop
}
else
{
- command = OS.PathWhich(ExecutableNames[editorId]);
+ command = OS.PathWhich(ExecutableNames[_editorId]);
}
}
else
{
- command = OS.PathWhich(ExecutableNames[editorId]);
+ command = OS.PathWhich(ExecutableNames[_editorId]);
}
args.Add("--ipc-tcp");
if (newWindow)
- args.Add("\"" + Path.GetFullPath(solutionFile) + "\"");
+ args.Add("\"" + Path.GetFullPath(_solutionFile) + "\"");
if (command == null)
throw new FileNotFoundException();
@@ -65,7 +65,7 @@ namespace GodotTools.Ides.MonoDevelop
if (newWindow)
{
- process = Process.Start(new ProcessStartInfo
+ _process = Process.Start(new ProcessStartInfo
{
FileName = command,
Arguments = string.Join(" ", args),
@@ -88,14 +88,14 @@ namespace GodotTools.Ides.MonoDevelop
if (editorId == EditorId.VisualStudioForMac && !OS.IsMacOS)
throw new InvalidOperationException($"{nameof(EditorId.VisualStudioForMac)} not supported on this platform");
- this.solutionFile = solutionFile;
- this.editorId = editorId;
+ _solutionFile = solutionFile;
+ _editorId = editorId;
}
public void Dispose()
{
IsDisposed = true;
- process?.Dispose();
+ _process?.Dispose();
}
private static readonly IReadOnlyDictionary<EditorId, string> ExecutableNames;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index 821532f759..71055f0125 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
@@ -11,6 +11,7 @@ using Environment = System.Environment;
using File = System.IO.File;
using Path = System.IO.Path;
using OS = GodotTools.Utils.OS;
+
// ReSharper disable UnassignedField.Local
// ReSharper disable InconsistentNaming
// ReSharper disable UnassignedField.Global
@@ -53,10 +54,10 @@ namespace GodotTools.Ides.Rider
private static RiderInfo[] CollectAllRiderPathsLinux()
{
var installInfos = new List<RiderInfo>();
- var home = Environment.GetEnvironmentVariable("HOME");
+ string home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrEmpty(home))
{
- var toolboxRiderRootPath = GetToolboxBaseDir();
+ string toolboxRiderRootPath = GetToolboxBaseDir();
installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
.Select(a => new RiderInfo(a, true)).ToList());
@@ -65,12 +66,12 @@ namespace GodotTools.Ides.Rider
if (shortcut.Exists)
{
- var lines = File.ReadAllLines(shortcut.FullName);
- foreach (var line in lines)
+ string[] lines = File.ReadAllLines(shortcut.FullName);
+ foreach (string line in lines)
{
if (!line.StartsWith("Exec=\""))
continue;
- var path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
+ string path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
if (string.IsNullOrEmpty(path))
continue;
@@ -82,7 +83,7 @@ namespace GodotTools.Ides.Rider
}
// snap install
- var snapInstallPath = "/snap/rider/current/bin/rider.sh";
+ string snapInstallPath = "/snap/rider/current/bin/rider.sh";
if (new FileInfo(snapInstallPath).Exists)
installInfos.Add(new RiderInfo(snapInstallPath, false));
@@ -98,15 +99,15 @@ namespace GodotTools.Ides.Rider
if (folder.Exists)
{
installInfos.AddRange(folder.GetDirectories("*Rider*.app")
- .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false))
- .ToList());
+ .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false))
+ .ToList());
}
// /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
// should be combined with "Contents/MacOS/rider"
- var toolboxRiderRootPath = GetToolboxBaseDir();
+ string toolboxRiderRootPath = GetToolboxBaseDir();
var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
- .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true));
+ .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true));
installInfos.AddRange(paths);
return installInfos.ToArray();
@@ -134,7 +135,7 @@ namespace GodotTools.Ides.Rider
{
if (OS.IsWindows)
{
- var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return GetToolboxRiderRootPath(localAppData);
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 60dd565ef2..ac29efb716 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -49,7 +49,7 @@ namespace GodotTools.Ides.Rider
if (!paths.Any())
return;
- var newPath = paths.Last().Path;
+ string newPath = paths.Last().Path;
Globals.EditorDef(EditorPathSettingName, newPath);
editorSettings.SetSetting(EditorPathSettingName, newPath);
}
@@ -57,7 +57,8 @@ namespace GodotTools.Ides.Rider
public static bool IsExternalEditorSetToRider(EditorSettings editorSettings)
{
- return editorSettings.HasSetting(EditorPathSettingName) && IsRider((string)editorSettings.GetSetting(EditorPathSettingName));
+ return editorSettings.HasSetting(EditorPathSettingName) &&
+ IsRider((string)editorSettings.GetSetting(EditorPathSettingName));
}
public static bool IsRider(string path)
@@ -66,7 +67,7 @@ namespace GodotTools.Ides.Rider
return false;
var fileInfo = new FileInfo(path);
- var filename = fileInfo.Name.ToLowerInvariant();
+ string filename = fileInfo.Name.ToLowerInvariant();
return filename.StartsWith("rider", StringComparison.Ordinal);
}
@@ -83,7 +84,7 @@ namespace GodotTools.Ides.Rider
if (!paths.Any())
return null;
- var newPath = paths.Last().Path;
+ string newPath = paths.Last().Path;
editorSettings.SetSetting(EditorPathSettingName, newPath);
Globals.EditorDef(EditorPathSettingName, newPath);
return newPath;
@@ -96,8 +97,8 @@ namespace GodotTools.Ides.Rider
public static void OpenFile(string slnPath, string scriptPath, int line)
{
- var pathFromSettings = GetRiderPathFromSettings();
- var path = CheckAndUpdatePath(pathFromSettings);
+ string pathFromSettings = GetRiderPathFromSettings();
+ string path = CheckAndUpdatePath(pathFromSettings);
var args = new List<string>();
args.Add(slnPath);
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
index 793f84fd77..5c5ced8c29 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs
@@ -13,6 +13,9 @@ namespace GodotTools.Internals
public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
internal_EditorDef(setting, defaultValue, restartIfChanged);
+ public static object EditorShortcut(string setting) =>
+ internal_EditorShortcut(setting);
+
[SuppressMessage("ReSharper", "InconsistentNaming")]
public static string TTR(this string text) => internal_TTR(text);
@@ -28,6 +31,9 @@ namespace GodotTools.Internals
private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
[MethodImpl(MethodImplOptions.InternalCall)]
+ private static extern object internal_EditorShortcut(string setting);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_TTR(string text);
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
index 6893bc1974..5e70c399b2 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
@@ -39,45 +39,57 @@ namespace GodotTools.Internals
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResDataDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResMetadataDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesBaseDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResAssembliesDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResConfigDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesBaseDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ResTempAssembliesDir();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoUserDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoLogsDir();
#region Tools-only
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoSolutionsDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_BuildLogsDirs();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectSlnPath();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_ProjectCsProjPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorToolsDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataEditorPrebuiltApiDir();
#endregion
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoEtcDir();
+
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_DataMonoLibDir();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
index c6724ccaf7..05499339b1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs
@@ -8,7 +8,7 @@ namespace GodotTools.Utils
{
public static class FsPathUtils
{
- private static readonly string ResourcePath = ProjectSettings.GlobalizePath("res://");
+ private static readonly string _resourcePath = ProjectSettings.GlobalizePath("res://");
private static bool PathStartsWithAlreadyNorm(this string childPath, string parentPath)
{
@@ -34,7 +34,7 @@ namespace GodotTools.Utils
public static string LocalizePathWithCaseChecked(string path)
{
string pathNorm = path.NormalizePath() + Path.DirectorySeparatorChar;
- string resourcePathNorm = ResourcePath.NormalizePath() + Path.DirectorySeparatorChar;
+ string resourcePathNorm = _resourcePath.NormalizePath() + Path.DirectorySeparatorChar;
if (!pathNorm.PathStartsWithAlreadyNorm(resourcePathNorm))
return null;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
index 4624439665..93a1360cb6 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -13,10 +13,10 @@ namespace GodotTools.Utils
public static class OS
{
[MethodImpl(MethodImplOptions.InternalCall)]
- static extern string GetPlatformName();
+ private static extern string GetPlatformName();
[MethodImpl(MethodImplOptions.InternalCall)]
- static extern bool UnixFileHasExecutableAccess(string filePath);
+ private static extern bool UnixFileHasExecutableAccess(string filePath);
public static class Names
{
@@ -106,7 +106,10 @@ namespace GodotTools.Utils
public static string PathWhich([NotNull] string name)
{
- return IsWindows ? PathWhichWindows(name) : PathWhichUnix(name);
+ if (IsWindows)
+ return PathWhichWindows(name);
+
+ return PathWhichUnix(name);
}
private static string PathWhichWindows([NotNull] string name)
@@ -129,7 +132,8 @@ namespace GodotTools.Utils
}
string nameExt = Path.GetExtension(name);
- bool hasPathExt = !string.IsNullOrEmpty(nameExt) && windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
+ bool hasPathExt = !string.IsNullOrEmpty(nameExt) &&
+ windowsExts.Contains(nameExt, StringComparer.OrdinalIgnoreCase);
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 7fdef8ff45..148a6796d2 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -365,8 +365,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append(link_target);
xml_output.append("</c>");
} else if (link_tag == "enum") {
- StringName search_cname = !target_itype ? target_cname :
- StringName(target_itype->name + "." + (String)target_cname);
+ StringName search_cname = !target_itype ? target_cname : StringName(target_itype->name + "." + (String)target_cname);
const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(search_cname);
@@ -387,7 +386,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
xml_output.append(link_target);
xml_output.append("</c>");
}
- } else if (link_tag == "const") {
+ } else if (link_tag == "constant") {
if (!target_itype || !target_itype->is_object_type) {
if (OS::get_singleton()->is_stdout_verbose()) {
if (target_itype) {
@@ -1524,7 +1523,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
if (getter->return_type.cname != setter_first_arg.type.cname) {
// Special case for Node::set_name
bool whitelisted = getter->return_type.cname == name_cache.type_StringName &&
- setter_first_arg.type.cname == name_cache.type_String;
+ setter_first_arg.type.cname == name_cache.type_String;
ERR_FAIL_COND_V_MSG(!whitelisted, ERR_BUG,
"Return type from getter doesn't match first argument of setter for property: '" +
@@ -2481,29 +2480,29 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
switch (p_val.get_type()) {
case Variant::NIL:
return p_arg_type.is_object_type ||
- name_cache.is_nullable_type(p_arg_type.name);
+ name_cache.is_nullable_type(p_arg_type.name);
case Variant::BOOL:
return p_arg_type.name == name_cache.type_bool;
case Variant::INT:
return p_arg_type.name == name_cache.type_sbyte ||
- p_arg_type.name == name_cache.type_short ||
- p_arg_type.name == name_cache.type_int ||
- p_arg_type.name == name_cache.type_byte ||
- p_arg_type.name == name_cache.type_ushort ||
- p_arg_type.name == name_cache.type_uint ||
- p_arg_type.name == name_cache.type_long ||
- p_arg_type.name == name_cache.type_ulong ||
- p_arg_type.name == name_cache.type_float ||
- p_arg_type.name == name_cache.type_double ||
- p_arg_type.is_enum;
+ p_arg_type.name == name_cache.type_short ||
+ p_arg_type.name == name_cache.type_int ||
+ p_arg_type.name == name_cache.type_byte ||
+ p_arg_type.name == name_cache.type_ushort ||
+ p_arg_type.name == name_cache.type_uint ||
+ p_arg_type.name == name_cache.type_long ||
+ p_arg_type.name == name_cache.type_ulong ||
+ p_arg_type.name == name_cache.type_float ||
+ p_arg_type.name == name_cache.type_double ||
+ p_arg_type.is_enum;
case Variant::FLOAT:
return p_arg_type.name == name_cache.type_float ||
- p_arg_type.name == name_cache.type_double;
+ p_arg_type.name == name_cache.type_double;
case Variant::STRING:
case Variant::STRING_NAME:
return p_arg_type.name == name_cache.type_String ||
- p_arg_type.name == name_cache.type_StringName ||
- p_arg_type.name == name_cache.type_NodePath;
+ p_arg_type.name == name_cache.type_StringName ||
+ p_arg_type.name == name_cache.type_NodePath;
case Variant::NODE_PATH:
return p_arg_type.name == name_cache.type_NodePath;
case Variant::TRANSFORM2D:
@@ -2535,13 +2534,13 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
return p_arg_type.is_object_type;
case Variant::VECTOR2I:
return p_arg_type.name == name_cache.type_Vector2 ||
- p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
case Variant::RECT2I:
return p_arg_type.name == name_cache.type_Rect2 ||
- p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
case Variant::VECTOR3I:
return p_arg_type.name == name_cache.type_Vector3 ||
- p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ p_arg_type.name == Variant::get_type_name(p_val.get_type());
default:
CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
break;
@@ -2610,7 +2609,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
Map<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) {
+ 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)) {
continue;
}
@@ -2714,7 +2713,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
if (itype.cname != name_cache.type_Object || imethod.name != "free") {
WARN_PRINT("Notification: New unexpected virtual non-overridable method found."
" We only expected Object.free, but found '" +
- itype.name + "." + imethod.name + "'.");
+ itype.name + "." + imethod.name + "'.");
}
} else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
imethod.return_type.cname = return_info.class_name;
@@ -2723,7 +2722,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
imethod.return_type.cname = return_info.class_name;
bool bad_reference_hint = !imethod.is_virtual && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE &&
- ClassDB::is_parent_class(return_info.class_name, name_cache.type_RefCounted);
+ ClassDB::is_parent_class(return_info.class_name, name_cache.type_RefCounted);
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 + "'.");
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 8a85a1acbd..a7879e96c8 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -575,7 +575,7 @@ class BindingsGenerator {
StaticCString::create(_STR(PackedByteArray)),
StaticCString::create(_STR(PackedInt32Array)),
- StaticCString::create(_STR(PackedInt64rray)),
+ StaticCString::create(_STR(PackedInt64Array)),
StaticCString::create(_STR(PackedFloat32Array)),
StaticCString::create(_STR(PackedFloat64Array)),
StaticCString::create(_STR(PackedStringArray)),
@@ -598,7 +598,7 @@ class BindingsGenerator {
private:
NameCache(const NameCache &);
- NameCache &operator=(const NameCache &);
+ void operator=(const NameCache &);
};
NameCache name_cache;
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index d911f6461c..61d0890288 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -123,8 +123,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
// AutoLoads
OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
- const ProjectSettings::AutoloadInfo &info = E.value;
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.value();
suggestions.push_back(quoted("/root/" + String(info.name)));
}
}
@@ -155,7 +155,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
dir_access->list_dir_begin();
String filename = dir_access->get_next();
- while (filename != "") {
+ while (!filename.is_empty()) {
if (filename == "." || filename == "..") {
filename = dir_access->get_next();
continue;
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 6692a6efec..9a61b63c12 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -306,6 +306,12 @@ MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_d
return GDMonoMarshal::variant_to_mono_object(result);
}
+MonoObject *godot_icall_Globals_EditorShortcut(MonoString *p_setting) {
+ String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+ Ref<Shortcut> result = ED_GET_SHORTCUT(setting);
+ return GDMonoMarshal::variant_to_mono_object(result);
+}
+
MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
String text = GDMonoMarshal::mono_string_to_godot(p_text);
return GDMonoMarshal::mono_string_from_godot(TTR(text));
@@ -380,6 +386,7 @@ void register_editor_internal_calls() {
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", godot_icall_Globals_EditorScale);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", godot_icall_Globals_GlobalDef);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", godot_icall_Globals_EditorDef);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_EditorShortcut", godot_icall_Globals_EditorShortcut);
GDMonoUtils::add_internal_call("GodotTools.Internals.Globals::internal_TTR", godot_icall_Globals_TTR);
// Utils.OS
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 1a3b81487f..850ae7fc3b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -20,7 +20,7 @@ namespace Godot
private Vector3 _size;
/// <summary>
- /// Beginning corner. Typically has values lower than End.
+ /// Beginning corner. Typically has values lower than <see cref="End"/>.
/// </summary>
/// <value>Directly uses a private field.</value>
public Vector3 Position
@@ -30,7 +30,7 @@ namespace Godot
}
/// <summary>
- /// Size from Position to End. Typically all components are positive.
+ /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
/// </summary>
/// <value>Directly uses a private field.</value>
@@ -44,7 +44,10 @@ namespace Godot
/// Ending corner. This is calculated as <see cref="Position"/> plus
/// <see cref="Size"/>. Setting this value will change the size.
/// </summary>
- /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
+ /// <value>
+ /// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
+ /// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
+ /// </value>
public Vector3 End
{
get { return _position + _size; }
@@ -52,10 +55,10 @@ namespace Godot
}
/// <summary>
- /// Returns an AABB with equivalent position and size, modified so that
+ /// Returns an <see cref="AABB"/> with equivalent position and size, modified so that
/// the most-negative corner is the origin and the size is positive.
/// </summary>
- /// <returns>The modified AABB.</returns>
+ /// <returns>The modified <see cref="AABB"/>.</returns>
public AABB Abs()
{
Vector3 end = End;
@@ -64,30 +67,42 @@ namespace Godot
}
/// <summary>
- /// Returns true if this AABB completely encloses another one.
+ /// Returns the center of the <see cref="AABB"/>, which is equal
+ /// to <see cref="Position"/> + (<see cref="Size"/> / 2).
/// </summary>
- /// <param name="with">The other AABB that may be enclosed.</param>
- /// <returns>A bool for whether or not this AABB encloses `b`.</returns>
+ /// <returns>The center.</returns>
+ public Vector3 GetCenter()
+ {
+ return _position + (_size * 0.5f);
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this <see cref="AABB"/> completely encloses another one.
+ /// </summary>
+ /// <param name="with">The other <see cref="AABB"/> that may be enclosed.</param>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not this <see cref="AABB"/> encloses <paramref name="with"/>.
+ /// </returns>
public bool Encloses(AABB with)
{
- Vector3 src_min = _position;
- Vector3 src_max = _position + _size;
- Vector3 dst_min = with._position;
- Vector3 dst_max = with._position + with._size;
+ Vector3 srcMin = _position;
+ Vector3 srcMax = _position + _size;
+ Vector3 dstMin = with._position;
+ Vector3 dstMax = with._position + with._size;
- return src_min.x <= dst_min.x &&
- src_max.x > dst_max.x &&
- src_min.y <= dst_min.y &&
- src_max.y > dst_max.y &&
- src_min.z <= dst_min.z &&
- src_max.z > dst_max.z;
+ return srcMin.x <= dstMin.x &&
+ srcMax.x > dstMax.x &&
+ srcMin.y <= dstMin.y &&
+ srcMax.y > dstMax.y &&
+ srcMin.z <= dstMin.z &&
+ srcMax.z > dstMax.z;
}
/// <summary>
- /// Returns this AABB expanded to include a given point.
+ /// Returns this <see cref="AABB"/> expanded to include a given point.
/// </summary>
/// <param name="point">The point to include.</param>
- /// <returns>The expanded AABB.</returns>
+ /// <returns>The expanded <see cref="AABB"/>.</returns>
public AABB Expand(Vector3 point)
{
Vector3 begin = _position;
@@ -123,7 +138,7 @@ namespace Godot
}
/// <summary>
- /// Returns the area of the AABB.
+ /// Returns the area of the <see cref="AABB"/>.
/// </summary>
/// <returns>The area.</returns>
public real_t GetArea()
@@ -132,10 +147,10 @@ namespace Godot
}
/// <summary>
- /// Gets the position of one of the 8 endpoints of the AABB.
+ /// Gets the position of one of the 8 endpoints of the <see cref="AABB"/>.
/// </summary>
/// <param name="idx">Which endpoint to get.</param>
- /// <returns>An endpoint of the AABB.</returns>
+ /// <returns>An endpoint of the <see cref="AABB"/>.</returns>
public Vector3 GetEndpoint(int idx)
{
switch (idx)
@@ -157,26 +172,29 @@ namespace Godot
case 7:
return new Vector3(_position.x + _size.x, _position.y + _size.y, _position.z + _size.z);
default:
- throw new ArgumentOutOfRangeException(nameof(idx), String.Format("Index is {0}, but a value from 0 to 7 is expected.", idx));
+ {
+ throw new ArgumentOutOfRangeException(nameof(idx),
+ $"Index is {idx}, but a value from 0 to 7 is expected.");
+ }
}
}
/// <summary>
- /// Returns the normalized longest axis of the AABB.
+ /// Returns the normalized longest axis of the <see cref="AABB"/>.
/// </summary>
- /// <returns>A vector representing the normalized longest axis of the AABB.</returns>
+ /// <returns>A vector representing the normalized longest axis of the <see cref="AABB"/>.</returns>
public Vector3 GetLongestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y > max_size)
+ if (_size.y > maxSize)
{
axis = new Vector3(0f, 1f, 0f);
- max_size = _size.y;
+ maxSize = _size.y;
}
- if (_size.z > max_size)
+ if (_size.z > maxSize)
{
axis = new Vector3(0f, 0f, 1f);
}
@@ -185,21 +203,21 @@ namespace Godot
}
/// <summary>
- /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the AABB.
+ /// Returns the <see cref="Vector3.Axis"/> index of the longest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is longest.</returns>
public Vector3.Axis GetLongestAxisIndex()
{
var axis = Vector3.Axis.X;
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y > max_size)
+ if (_size.y > maxSize)
{
axis = Vector3.Axis.Y;
- max_size = _size.y;
+ maxSize = _size.y;
}
- if (_size.z > max_size)
+ if (_size.z > maxSize)
{
axis = Vector3.Axis.Z;
}
@@ -208,38 +226,38 @@ namespace Godot
}
/// <summary>
- /// Returns the scalar length of the longest axis of the AABB.
+ /// Returns the scalar length of the longest axis of the <see cref="AABB"/>.
/// </summary>
- /// <returns>The scalar length of the longest axis of the AABB.</returns>
+ /// <returns>The scalar length of the longest axis of the <see cref="AABB"/>.</returns>
public real_t GetLongestAxisSize()
{
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y > max_size)
- max_size = _size.y;
+ if (_size.y > maxSize)
+ maxSize = _size.y;
- if (_size.z > max_size)
- max_size = _size.z;
+ if (_size.z > maxSize)
+ maxSize = _size.z;
- return max_size;
+ return maxSize;
}
/// <summary>
- /// Returns the normalized shortest axis of the AABB.
+ /// Returns the normalized shortest axis of the <see cref="AABB"/>.
/// </summary>
- /// <returns>A vector representing the normalized shortest axis of the AABB.</returns>
+ /// <returns>A vector representing the normalized shortest axis of the <see cref="AABB"/>.</returns>
public Vector3 GetShortestAxis()
{
var axis = new Vector3(1f, 0f, 0f);
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y < max_size)
+ if (_size.y < maxSize)
{
axis = new Vector3(0f, 1f, 0f);
- max_size = _size.y;
+ maxSize = _size.y;
}
- if (_size.z < max_size)
+ if (_size.z < maxSize)
{
axis = new Vector3(0f, 0f, 1f);
}
@@ -248,21 +266,21 @@ namespace Godot
}
/// <summary>
- /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the AABB.
+ /// Returns the <see cref="Vector3.Axis"/> index of the shortest axis of the <see cref="AABB"/>.
/// </summary>
/// <returns>A <see cref="Vector3.Axis"/> index for which axis is shortest.</returns>
public Vector3.Axis GetShortestAxisIndex()
{
var axis = Vector3.Axis.X;
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y < max_size)
+ if (_size.y < maxSize)
{
axis = Vector3.Axis.Y;
- max_size = _size.y;
+ maxSize = _size.y;
}
- if (_size.z < max_size)
+ if (_size.z < maxSize)
{
axis = Vector3.Axis.Z;
}
@@ -271,20 +289,20 @@ namespace Godot
}
/// <summary>
- /// Returns the scalar length of the shortest axis of the AABB.
+ /// Returns the scalar length of the shortest axis of the <see cref="AABB"/>.
/// </summary>
- /// <returns>The scalar length of the shortest axis of the AABB.</returns>
+ /// <returns>The scalar length of the shortest axis of the <see cref="AABB"/>.</returns>
public real_t GetShortestAxisSize()
{
- real_t max_size = _size.x;
+ real_t maxSize = _size.x;
- if (_size.y < max_size)
- max_size = _size.y;
+ if (_size.y < maxSize)
+ maxSize = _size.y;
- if (_size.z < max_size)
- max_size = _size.z;
+ if (_size.z < maxSize)
+ maxSize = _size.z;
- return max_size;
+ return maxSize;
}
/// <summary>
@@ -295,23 +313,23 @@ namespace Godot
/// <returns>A vector representing the support.</returns>
public Vector3 GetSupport(Vector3 dir)
{
- Vector3 half_extents = _size * 0.5f;
- Vector3 ofs = _position + half_extents;
+ Vector3 halfExtents = _size * 0.5f;
+ Vector3 ofs = _position + halfExtents;
return ofs + new Vector3(
- dir.x > 0f ? -half_extents.x : half_extents.x,
- dir.y > 0f ? -half_extents.y : half_extents.y,
- dir.z > 0f ? -half_extents.z : half_extents.z);
+ dir.x > 0f ? -halfExtents.x : halfExtents.x,
+ dir.y > 0f ? -halfExtents.y : halfExtents.y,
+ dir.z > 0f ? -halfExtents.z : halfExtents.z);
}
/// <summary>
- /// Returns a copy of the AABB grown a given amount of units towards all the sides.
+ /// Returns a copy of the <see cref="AABB"/> grown a given amount of units towards all the sides.
/// </summary>
/// <param name="by">The amount to grow by.</param>
- /// <returns>The grown AABB.</returns>
+ /// <returns>The grown <see cref="AABB"/>.</returns>
public AABB Grow(real_t by)
{
- var res = this;
+ AABB res = this;
res._position.x -= by;
res._position.y -= by;
@@ -324,28 +342,37 @@ namespace Godot
}
/// <summary>
- /// Returns true if the AABB is flat or empty, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> is flat or empty,
+ /// or <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool for whether or not the AABB has area.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area.
+ /// </returns>
public bool HasNoArea()
{
return _size.x <= 0f || _size.y <= 0f || _size.z <= 0f;
}
/// <summary>
- /// Returns true if the AABB has no surface (no size), or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> has no surface (no size),
+ /// or <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool for whether or not the AABB has area.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> has area.
+ /// </returns>
public bool HasNoSurface()
{
return _size.x <= 0f && _size.y <= 0f && _size.z <= 0f;
}
/// <summary>
- /// Returns true if the AABB contains a point, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> contains a point,
+ /// or <see langword="false"/> otherwise.
/// </summary>
/// <param name="point">The point to check.</param>
- /// <returns>A bool for whether or not the AABB contains `point`.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> contains <paramref name="point"/>.
+ /// </returns>
public bool HasPoint(Vector3 point)
{
if (point.x < _position.x)
@@ -365,56 +392,59 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection of this AABB and `b`.
+ /// Returns the intersection of this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
- /// <param name="with">The other AABB.</param>
- /// <returns>The clipped AABB.</returns>
+ /// <param name="with">The other <see cref="AABB"/>.</param>
+ /// <returns>The clipped <see cref="AABB"/>.</returns>
public AABB Intersection(AABB with)
{
- Vector3 src_min = _position;
- Vector3 src_max = _position + _size;
- Vector3 dst_min = with._position;
- Vector3 dst_max = with._position + with._size;
+ Vector3 srcMin = _position;
+ Vector3 srcMax = _position + _size;
+ Vector3 dstMin = with._position;
+ Vector3 dstMax = with._position + with._size;
Vector3 min, max;
- if (src_min.x > dst_max.x || src_max.x < dst_min.x)
+ if (srcMin.x > dstMax.x || srcMax.x < dstMin.x)
{
return new AABB();
}
- min.x = src_min.x > dst_min.x ? src_min.x : dst_min.x;
- max.x = src_max.x < dst_max.x ? src_max.x : dst_max.x;
+ min.x = srcMin.x > dstMin.x ? srcMin.x : dstMin.x;
+ max.x = srcMax.x < dstMax.x ? srcMax.x : dstMax.x;
- if (src_min.y > dst_max.y || src_max.y < dst_min.y)
+ if (srcMin.y > dstMax.y || srcMax.y < dstMin.y)
{
return new AABB();
}
- min.y = src_min.y > dst_min.y ? src_min.y : dst_min.y;
- max.y = src_max.y < dst_max.y ? src_max.y : dst_max.y;
+ min.y = srcMin.y > dstMin.y ? srcMin.y : dstMin.y;
+ max.y = srcMax.y < dstMax.y ? srcMax.y : dstMax.y;
- if (src_min.z > dst_max.z || src_max.z < dst_min.z)
+ if (srcMin.z > dstMax.z || srcMax.z < dstMin.z)
{
return new AABB();
}
- min.z = src_min.z > dst_min.z ? src_min.z : dst_min.z;
- max.z = src_max.z < dst_max.z ? src_max.z : dst_max.z;
+ min.z = srcMin.z > dstMin.z ? srcMin.z : dstMin.z;
+ max.z = srcMax.z < dstMax.z ? srcMax.z : dstMax.z;
return new AABB(min, max - min);
}
/// <summary>
- /// Returns true if the AABB overlaps with `b`
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> overlaps with <paramref name="with"/>
/// (i.e. they have at least one point in common).
///
- /// If `includeBorders` is true, they will also be considered overlapping
- /// if their borders touch, even without intersection.
+ /// If <paramref name="includeBorders"/> is <see langword="true"/>,
+ /// they will also be considered overlapping if their borders touch,
+ /// even without intersection.
/// </summary>
- /// <param name="with">The other AABB to check for intersections with.</param>
+ /// <param name="with">The other <see cref="AABB"/> to check for intersections with.</param>
/// <param name="includeBorders">Whether or not to consider borders.</param>
- /// <returns>A bool for whether or not they are intersecting.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not they are intersecting.
+ /// </returns>
public bool Intersects(AABB with, bool includeBorders = false)
{
if (includeBorders)
@@ -452,10 +482,12 @@ namespace Godot
}
/// <summary>
- /// Returns true if the AABB is on both sides of `plane`.
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> is on both sides of <paramref name="plane"/>.
/// </summary>
- /// <param name="plane">The plane to check for intersection.</param>
- /// <returns>A bool for whether or not the AABB intersects the plane.</returns>
+ /// <param name="plane">The <see cref="Plane"/> to check for intersection.</param>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the <see cref="Plane"/>.
+ /// </returns>
public bool IntersectsPlane(Plane plane)
{
Vector3[] points =
@@ -489,11 +521,14 @@ namespace Godot
}
/// <summary>
- /// Returns true if the AABB intersects the line segment between `from` and `to`.
+ /// Returns <see langword="true"/> if the <see cref="AABB"/> intersects
+ /// the line segment between <paramref name="from"/> and <paramref name="to"/>.
/// </summary>
/// <param name="from">The start of the line segment.</param>
/// <param name="to">The end of the line segment.</param>
- /// <returns>A bool for whether or not the AABB intersects the line segment.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="AABB"/> intersects the line segment.
+ /// </returns>
public bool IntersectsSegment(Vector3 from, Vector3 to)
{
real_t min = 0f;
@@ -549,10 +584,10 @@ namespace Godot
}
/// <summary>
- /// Returns a larger AABB that contains this AABB and `b`.
+ /// Returns a larger <see cref="AABB"/> that contains this <see cref="AABB"/> and <paramref name="with"/>.
/// </summary>
- /// <param name="with">The other AABB.</param>
- /// <returns>The merged AABB.</returns>
+ /// <param name="with">The other <see cref="AABB"/>.</param>
+ /// <returns>The merged <see cref="AABB"/>.</returns>
public AABB Merge(AABB with)
{
Vector3 beg1 = _position;
@@ -561,22 +596,22 @@ namespace Godot
var end2 = new Vector3(with._size.x, with._size.y, with._size.z) + beg2;
var min = new Vector3(
- beg1.x < beg2.x ? beg1.x : beg2.x,
- beg1.y < beg2.y ? beg1.y : beg2.y,
- beg1.z < beg2.z ? beg1.z : beg2.z
- );
+ beg1.x < beg2.x ? beg1.x : beg2.x,
+ beg1.y < beg2.y ? beg1.y : beg2.y,
+ beg1.z < beg2.z ? beg1.z : beg2.z
+ );
var max = new Vector3(
- end1.x > end2.x ? end1.x : end2.x,
- end1.y > end2.y ? end1.y : end2.y,
- end1.z > end2.z ? end1.z : end2.z
- );
+ end1.x > end2.x ? end1.x : end2.x,
+ end1.y > end2.y ? end1.y : end2.y,
+ end1.z > end2.z ? end1.z : end2.z
+ );
return new AABB(min, max - min);
}
/// <summary>
- /// Constructs an AABB from a position and size.
+ /// Constructs an <see cref="AABB"/> from a position and size.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="size">The size, typically positive.</param>
@@ -587,7 +622,8 @@ namespace Godot
}
/// <summary>
- /// Constructs an AABB from a position, width, height, and depth.
+ /// Constructs an <see cref="AABB"/> from a <paramref name="position"/>,
+ /// <paramref name="width"/>, <paramref name="height"/>, and <paramref name="depth"/>.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="width">The width, typically positive.</param>
@@ -600,7 +636,8 @@ namespace Godot
}
/// <summary>
- /// Constructs an AABB from x, y, z, and size.
+ /// Constructs an <see cref="AABB"/> from <paramref name="x"/>,
+ /// <paramref name="y"/>, <paramref name="z"/>, and <paramref name="size"/>.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -613,7 +650,9 @@ namespace Godot
}
/// <summary>
- /// Constructs an AABB from x, y, z, width, height, and depth.
+ /// Constructs an <see cref="AABB"/> from <paramref name="x"/>,
+ /// <paramref name="y"/>, <paramref name="z"/>, <paramref name="width"/>,
+ /// <paramref name="height"/>, and <paramref name="depth"/>.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -627,16 +666,40 @@ namespace Godot
_size = new Vector3(width, height, depth);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the AABBs are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left AABB.</param>
+ /// <param name="right">The right AABB.</param>
+ /// <returns>Whether or not the AABBs are exactly equal.</returns>
public static bool operator ==(AABB left, AABB right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the AABBs are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left AABB.</param>
+ /// <param name="right">The right AABB.</param>
+ /// <returns>Whether or not the AABBs are not equal.</returns>
public static bool operator !=(AABB left, AABB right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the AABB is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the AABB and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is AABB)
@@ -647,32 +710,51 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the AABBs are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other AABB.</param>
+ /// <returns>Whether or not the AABBs are exactly equal.</returns>
public bool Equals(AABB other)
{
return _position == other._position && _size == other._size;
}
/// <summary>
- /// Returns true if this AABB and `other` are approximately equal, by running
- /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// Returns <see langword="true"/> if this AABB and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
/// </summary>
/// <param name="other">The other AABB to compare.</param>
- /// <returns>Whether or not the AABBs are approximately equal.</returns>
+ /// <returns>Whether or not the AABBs structures are approximately equal.</returns>
public bool IsEqualApprox(AABB other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other._size);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="AABB"/>.
+ /// </summary>
+ /// <returns>A hash code for this AABB.</returns>
public override int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="AABB"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this AABB.</returns>
public override string ToString()
{
return $"{_position}, {_size}";
}
+ /// <summary>
+ /// Converts this <see cref="AABB"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this AABB.</returns>
public string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
index f52a767018..a412047196 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs
@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace Godot.Collections
{
- class ArraySafeHandle : SafeHandle
+ internal class ArraySafeHandle : SafeHandle
{
public ArraySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
@@ -33,15 +33,15 @@ namespace Godot.Collections
/// </summary>
public class Array : IList, IDisposable
{
- ArraySafeHandle safeHandle;
- bool disposed = false;
+ private ArraySafeHandle _safeHandle;
+ private bool _disposed = false;
/// <summary>
/// Constructs a new empty <see cref="Array"/>.
/// </summary>
public Array()
{
- safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
+ _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor());
}
/// <summary>
@@ -69,31 +69,31 @@ namespace Godot.Collections
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
- safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
+ _safeHandle = new ArraySafeHandle(godot_icall_Array_Ctor_MonoArray(array));
}
internal Array(ArraySafeHandle handle)
{
- safeHandle = handle;
+ _safeHandle = handle;
}
internal Array(IntPtr handle)
{
- safeHandle = new ArraySafeHandle(handle);
+ _safeHandle = new ArraySafeHandle(handle);
}
internal IntPtr GetPtr()
{
- if (disposed)
+ if (_disposed)
throw new ObjectDisposedException(GetType().FullName);
- return safeHandle.DangerousGetHandle();
+ return _safeHandle.DangerousGetHandle();
}
/// <summary>
/// Duplicates this <see cref="Array"/>.
/// </summary>
- /// <param name="deep">If true, performs a deep copy.</param>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
/// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
@@ -136,16 +136,16 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
- if (disposed)
+ if (_disposed)
return;
- if (safeHandle != null)
+ if (_safeHandle != null)
{
- safeHandle.Dispose();
- safeHandle = null;
+ _safeHandle.Dispose();
+ _safeHandle = null;
}
- disposed = true;
+ _disposed = true;
}
// IList
@@ -155,9 +155,9 @@ namespace Godot.Collections
bool IList.IsFixedSize => false;
/// <summary>
- /// Returns the object at the given index.
+ /// Returns the object at the given <paramref name="index"/>.
/// </summary>
- /// <value>The object at the given index.</value>
+ /// <value>The object at the given <paramref name="index"/>.</value>
public object this[int index]
{
get => godot_icall_Array_At(GetPtr(), index);
@@ -166,7 +166,7 @@ namespace Godot.Collections
/// <summary>
/// Adds an object to the end of this <see cref="Array"/>.
- /// This is the same as `append` or `push_back` in GDScript.
+ /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
/// <param name="value">The object to add.</param>
/// <returns>The new size after adding the object.</returns>
@@ -203,7 +203,7 @@ namespace Godot.Collections
public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
/// <summary>
- /// Removes the first occurrence of the specified value
+ /// Removes the first occurrence of the specified <paramref name="value"/>
/// from this <see cref="Array"/>.
/// </summary>
/// <param name="value">The value to remove.</param>
@@ -272,67 +272,67 @@ namespace Godot.Collections
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Ctor();
+ internal static extern IntPtr godot_icall_Array_Ctor();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
+ internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
+ internal static extern void godot_icall_Array_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
+ internal static extern object godot_icall_Array_At(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
+ internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
+ internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_Count(IntPtr ptr);
+ internal static extern int godot_icall_Array_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_Add(IntPtr ptr, object item);
+ internal static extern int godot_icall_Array_Add(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Clear(IntPtr ptr);
+ internal static extern void godot_icall_Array_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
+ internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
+ internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
+ internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
+ internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
+ internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
+ internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
+ internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
+ internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
+ internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Error godot_icall_Array_Shuffle(IntPtr ptr);
+ internal static extern Error godot_icall_Array_Shuffle(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
+ internal static extern void godot_icall_Array_Generic_GetElementTypeInfo(Type elemType, out int elemTypeEncoding, out IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_Array_ToString(IntPtr ptr);
+ internal static extern string godot_icall_Array_ToString(IntPtr ptr);
}
/// <summary>
@@ -344,7 +344,7 @@ namespace Godot.Collections
/// <typeparam name="T">The type of the array.</typeparam>
public class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>
{
- Array objectArray;
+ private Array _objectArray;
internal static int elemTypeEncoding;
internal static IntPtr elemTypeClass;
@@ -359,7 +359,7 @@ namespace Godot.Collections
/// </summary>
public Array()
{
- objectArray = new Array();
+ _objectArray = new Array();
}
/// <summary>
@@ -372,7 +372,7 @@ namespace Godot.Collections
if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
- objectArray = new Array(collection);
+ _objectArray = new Array(collection);
}
/// <summary>
@@ -386,7 +386,7 @@ namespace Godot.Collections
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
- objectArray = new Array(array);
+ _objectArray = new Array(array);
}
/// <summary>
@@ -395,22 +395,22 @@ namespace Godot.Collections
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
- objectArray = array;
+ _objectArray = array;
}
internal Array(IntPtr handle)
{
- objectArray = new Array(handle);
+ _objectArray = new Array(handle);
}
internal Array(ArraySafeHandle handle)
{
- objectArray = new Array(handle);
+ _objectArray = new Array(handle);
}
internal IntPtr GetPtr()
{
- return objectArray.GetPtr();
+ return _objectArray.GetPtr();
}
/// <summary>
@@ -419,17 +419,17 @@ namespace Godot.Collections
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
- return from.objectArray;
+ return from._objectArray;
}
/// <summary>
/// Duplicates this <see cref="Array{T}"/>.
/// </summary>
- /// <param name="deep">If true, performs a deep copy.</param>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
- return new Array<T>(objectArray.Duplicate(deep));
+ return new Array<T>(_objectArray.Duplicate(deep));
}
/// <summary>
@@ -439,7 +439,7 @@ namespace Godot.Collections
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
- return objectArray.Resize(newSize);
+ return _objectArray.Resize(newSize);
}
/// <summary>
@@ -447,7 +447,7 @@ namespace Godot.Collections
/// </summary>
public void Shuffle()
{
- objectArray.Shuffle();
+ _objectArray.Shuffle();
}
/// <summary>
@@ -458,19 +458,19 @@ namespace Godot.Collections
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
- return new Array<T>(left.objectArray + right.objectArray);
+ return new Array<T>(left._objectArray + right._objectArray);
}
// IList<T>
/// <summary>
- /// Returns the value at the given index.
+ /// Returns the value at the given <paramref name="index"/>.
/// </summary>
- /// <value>The value at the given index.</value>
+ /// <value>The value at the given <paramref name="index"/>.</value>
public T this[int index]
{
get { return (T)Array.godot_icall_Array_At_Generic(GetPtr(), index, elemTypeEncoding, elemTypeClass); }
- set { objectArray[index] = value; }
+ set { _objectArray[index] = value; }
}
/// <summary>
@@ -481,7 +481,7 @@ namespace Godot.Collections
/// <returns>The index of the item, or -1 if not found.</returns>
public int IndexOf(T item)
{
- return objectArray.IndexOf(item);
+ return _objectArray.IndexOf(item);
}
/// <summary>
@@ -494,7 +494,7 @@ namespace Godot.Collections
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
- objectArray.Insert(index, item);
+ _objectArray.Insert(index, item);
}
/// <summary>
@@ -503,7 +503,7 @@ namespace Godot.Collections
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
- objectArray.RemoveAt(index);
+ _objectArray.RemoveAt(index);
}
// ICollection<T>
@@ -515,20 +515,20 @@ namespace Godot.Collections
/// <returns>The number of elements.</returns>
public int Count
{
- get { return objectArray.Count; }
+ get { return _objectArray.Count; }
}
bool ICollection<T>.IsReadOnly => false;
/// <summary>
/// Adds an item to the end of this <see cref="Array{T}"/>.
- /// This is the same as `append` or `push_back` in GDScript.
+ /// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
public void Add(T item)
{
- objectArray.Add(item);
+ _objectArray.Add(item);
}
/// <summary>
@@ -536,7 +536,7 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
- objectArray.Clear();
+ _objectArray.Clear();
}
/// <summary>
@@ -546,7 +546,7 @@ namespace Godot.Collections
/// <returns>Whether or not this array contains the given item.</returns>
public bool Contains(T item)
{
- return objectArray.Contains(item);
+ return _objectArray.Contains(item);
}
/// <summary>
@@ -566,7 +566,7 @@ namespace Godot.Collections
// TODO This may be quite slow because every element access is an internal call.
// It could be moved entirely to an internal call if we find out how to do the cast there.
- int count = objectArray.Count;
+ int count = _objectArray.Count;
if (array.Length < (arrayIndex + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
@@ -583,7 +583,7 @@ namespace Godot.Collections
/// from this <see cref="Array{T}"/>.
/// </summary>
/// <param name="item">The value to remove.</param>
- /// <returns>A bool indicating success or failure.</returns>
+ /// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
return Array.godot_icall_Array_Remove(GetPtr(), item);
@@ -597,7 +597,7 @@ namespace Godot.Collections
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
- int count = objectArray.Count;
+ int count = _objectArray.Count;
for (int i = 0; i < count; i++)
{
@@ -614,6 +614,6 @@ namespace Godot.Collections
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
- public override string ToString() => objectArray.ToString();
+ public override string ToString() => _objectArray.ToString();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
index ac6cffceb2..e93bc89811 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
@@ -3,7 +3,5 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Class)]
- public class DisableGodotGeneratorsAttribute : Attribute
- {
- }
+ public class DisableGodotGeneratorsAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
index 8aaa9e849d..b8b9bc660c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
@@ -3,8 +3,8 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Method)]
- public class RemoteAttribute : Attribute {}
+ public class AnyPeerAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
- public class PuppetAttribute : Attribute {}
+ public class AuthorityAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
index 39d5782db8..07a214f543 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/SignalAttribute.cs
@@ -3,7 +3,5 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Delegate | AttributeTargets.Event)]
- public class SignalAttribute : Attribute
- {
- }
+ public class SignalAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
index d0437409af..d2344389f4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ToolAttribute.cs
@@ -3,5 +3,5 @@ using System;
namespace Godot
{
[AttributeUsage(AttributeTargets.Class)]
- public class ToolAttribute : Attribute {}
+ public class ToolAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 968f853c2d..656796c5c7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -31,7 +31,7 @@ namespace Godot
/// <summary>
/// The basis matrix's X vector (column 0).
/// </summary>
- /// <value>Equivalent to <see cref="Column0"/> and array index `[0]`.</value>
+ /// <value>Equivalent to <see cref="Column0"/> and array index <c>[0]</c>.</value>
public Vector3 x
{
get => Column0;
@@ -41,7 +41,7 @@ namespace Godot
/// <summary>
/// The basis matrix's Y vector (column 1).
/// </summary>
- /// <value>Equivalent to <see cref="Column1"/> and array index `[1]`.</value>
+ /// <value>Equivalent to <see cref="Column1"/> and array index <c>[1]</c>.</value>
public Vector3 y
{
get => Column1;
@@ -51,7 +51,7 @@ namespace Godot
/// <summary>
/// The basis matrix's Z vector (column 2).
/// </summary>
- /// <value>Equivalent to <see cref="Column2"/> and array index `[2]`.</value>
+ /// <value>Equivalent to <see cref="Column2"/> and array index <c>[2]</c>.</value>
public Vector3 z
{
get => Column2;
@@ -82,45 +82,45 @@ namespace Godot
/// <summary>
/// Column 0 of the basis matrix (the X vector).
/// </summary>
- /// <value>Equivalent to <see cref="x"/> and array index `[0]`.</value>
+ /// <value>Equivalent to <see cref="x"/> and array index <c>[0]</c>.</value>
public Vector3 Column0
{
get => new Vector3(Row0.x, Row1.x, Row2.x);
set
{
- this.Row0.x = value.x;
- this.Row1.x = value.y;
- this.Row2.x = value.z;
+ Row0.x = value.x;
+ Row1.x = value.y;
+ Row2.x = value.z;
}
}
/// <summary>
/// Column 1 of the basis matrix (the Y vector).
/// </summary>
- /// <value>Equivalent to <see cref="y"/> and array index `[1]`.</value>
+ /// <value>Equivalent to <see cref="y"/> and array index <c>[1]</c>.</value>
public Vector3 Column1
{
get => new Vector3(Row0.y, Row1.y, Row2.y);
set
{
- this.Row0.y = value.x;
- this.Row1.y = value.y;
- this.Row2.y = value.z;
+ Row0.y = value.x;
+ Row1.y = value.y;
+ Row2.y = value.z;
}
}
/// <summary>
/// Column 2 of the basis matrix (the Z vector).
/// </summary>
- /// <value>Equivalent to <see cref="z"/> and array index `[2]`.</value>
+ /// <value>Equivalent to <see cref="z"/> and array index <c>[2]</c>.</value>
public Vector3 Column2
{
get => new Vector3(Row0.z, Row1.z, Row2.z);
set
{
- this.Row0.z = value.x;
- this.Row1.z = value.y;
- this.Row2.z = value.z;
+ Row0.z = value.x;
+ Row1.z = value.y;
+ Row2.z = value.z;
}
}
@@ -150,9 +150,10 @@ namespace Godot
}
/// <summary>
- /// Access whole columns in the form of Vector3.
+ /// Access whole columns in the form of <see cref="Vector3"/>.
/// </summary>
/// <param name="column">Which column vector.</param>
+ /// <value>The basis column.</value>
public Vector3 this[int column]
{
get
@@ -193,6 +194,7 @@ namespace Godot
/// </summary>
/// <param name="column">Which column, the matrix horizontal position.</param>
/// <param name="row">Which row, the matrix vertical position.</param>
+ /// <value>The matrix element.</value>
public real_t this[int column, int row]
{
get
@@ -207,20 +209,6 @@ namespace Godot
}
}
- public Quaternion GetRotationQuaternion()
- {
- Basis orthonormalizedBasis = Orthonormalized();
- real_t det = orthonormalizedBasis.Determinant();
- if (det < 0)
- {
- // Ensure that the determinant is 1, such that result is a proper
- // rotation matrix which can be represented by Euler angles.
- orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One);
- }
-
- return orthonormalizedBasis.Quaternion();
- }
-
internal void SetQuaternionScale(Quaternion quaternion, Vector3 scale)
{
SetDiagonal(scale);
@@ -263,10 +251,10 @@ namespace Godot
/// The returned vector contains the rotation angles in
/// the format (X angle, Y angle, Z angle).
///
- /// Consider using the <see cref="Basis.Quaternion()"/> method instead, which
- /// returns a <see cref="Godot.Quaternion"/> quaternion instead of Euler angles.
+ /// Consider using the <see cref="GetRotationQuaternion"/> method instead, which
+ /// returns a <see cref="Quaternion"/> quaternion instead of Euler angles.
/// </summary>
- /// <returns>A Vector3 representing the basis rotation in Euler angles.</returns>
+ /// <returns>A <see cref="Vector3"/> representing the basis rotation in Euler angles.</returns>
public Vector3 GetEuler()
{
Basis m = Orthonormalized();
@@ -300,11 +288,93 @@ namespace Godot
}
/// <summary>
+ /// Returns the basis's rotation in the form of a quaternion.
+ /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
+ /// mind that quaternions should generally be preferred to Euler angles.
+ /// </summary>
+ /// <returns>A <see cref="Quaternion"/> representing the basis's rotation.</returns>
+ internal Quaternion GetQuaternion()
+ {
+ real_t trace = Row0[0] + Row1[1] + Row2[2];
+
+ if (trace > 0.0f)
+ {
+ real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quaternion(
+ (Row2[1] - Row1[2]) * inv_s,
+ (Row0[2] - Row2[0]) * inv_s,
+ (Row1[0] - Row0[1]) * inv_s,
+ s * 0.25f
+ );
+ }
+
+ if (Row0[0] > Row1[1] && Row0[0] > Row2[2])
+ {
+ real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quaternion(
+ s * 0.25f,
+ (Row0[1] + Row1[0]) * inv_s,
+ (Row0[2] + Row2[0]) * inv_s,
+ (Row2[1] - Row1[2]) * inv_s
+ );
+ }
+
+ if (Row1[1] > Row2[2])
+ {
+ real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quaternion(
+ (Row0[1] + Row1[0]) * inv_s,
+ s * 0.25f,
+ (Row1[2] + Row2[1]) * inv_s,
+ (Row0[2] - Row2[0]) * inv_s
+ );
+ }
+ else
+ {
+ real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quaternion(
+ (Row0[2] + Row2[0]) * inv_s,
+ (Row1[2] + Row2[1]) * inv_s,
+ s * 0.25f,
+ (Row1[0] - Row0[1]) * inv_s
+ );
+ }
+ }
+
+ /// <summary>
+ /// Returns the <see cref="Basis"/>'s rotation in the form of a
+ /// <see cref="Quaternion"/>. See <see cref="GetEuler"/> if you
+ /// need Euler angles, but keep in mind quaternions should generally
+ /// be preferred to Euler angles.
+ /// </summary>
+ /// <returns>The basis rotation.</returns>
+ public Quaternion GetRotationQuaternion()
+ {
+ Basis orthonormalizedBasis = Orthonormalized();
+ real_t det = orthonormalizedBasis.Determinant();
+ if (det < 0)
+ {
+ // Ensure that the determinant is 1, such that result is a proper
+ // rotation matrix which can be represented by Euler angles.
+ orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One);
+ }
+
+ return orthonormalizedBasis.GetQuaternion();
+ }
+
+ /// <summary>
/// Get rows by index. Rows are not very useful for user code,
/// but are more efficient for some internal calculations.
/// </summary>
/// <param name="index">Which row.</param>
- /// <returns>One of `Row0`, `Row1`, or `Row2`.</returns>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the <paramref name="index"/> is not 0, 1 or 2.
+ /// </exception>
+ /// <returns>One of <c>Row0</c>, <c>Row1</c>, or <c>Row2</c>.</returns>
public Vector3 GetRow(int index)
{
switch (index)
@@ -326,6 +396,9 @@ namespace Godot
/// </summary>
/// <param name="index">Which row.</param>
/// <param name="value">The vector to set the row to.</param>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the <paramref name="index"/> is not 0, 1 or 2.
+ /// </exception>
public void SetRow(int index, Vector3 value)
{
switch (index)
@@ -452,8 +525,8 @@ namespace Godot
}
/// <summary>
- /// Introduce an additional rotation around the given `axis`
- /// by `phi` (in radians). The axis must be a normalized vector.
+ /// Introduce an additional rotation around the given <paramref name="axis"/>
+ /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
/// <param name="phi">The angle to rotate, in radians.</param>
@@ -504,7 +577,7 @@ namespace Godot
/// <returns>The resulting dot product.</returns>
public real_t Tdotx(Vector3 with)
{
- return this.Row0[0] * with[0] + this.Row1[0] * with[1] + this.Row2[0] * with[2];
+ return Row0[0] * with[0] + Row1[0] * with[1] + Row2[0] * with[2];
}
/// <summary>
@@ -514,7 +587,7 @@ namespace Godot
/// <returns>The resulting dot product.</returns>
public real_t Tdoty(Vector3 with)
{
- return this.Row0[1] * with[0] + this.Row1[1] * with[1] + this.Row2[1] * with[2];
+ return Row0[1] * with[0] + Row1[1] * with[1] + Row2[1] * with[2];
}
/// <summary>
@@ -524,7 +597,7 @@ namespace Godot
/// <returns>The resulting dot product.</returns>
public real_t Tdotz(Vector3 with)
{
- return this.Row0[2] * with[0] + this.Row1[2] * with[1] + this.Row2[2] * with[2];
+ return Row0[2] * with[0] + Row1[2] * with[1] + Row2[2] * with[2];
}
/// <summary>
@@ -533,7 +606,7 @@ namespace Godot
/// <returns>The transposed basis matrix.</returns>
public Basis Transposed()
{
- var tr = this;
+ Basis tr = this;
real_t temp = tr.Row0[1];
tr.Row0[1] = tr.Row1[0];
@@ -553,15 +626,16 @@ namespace Godot
/// <summary>
/// Returns a vector transformed (multiplied) by the basis matrix.
/// </summary>
+ /// <seealso cref="XformInv(Vector3)"/>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
{
return new Vector3
(
- this.Row0.Dot(v),
- this.Row1.Dot(v),
- this.Row2.Dot(v)
+ Row0.Dot(v),
+ Row1.Dot(v),
+ Row2.Dot(v)
);
}
@@ -571,76 +645,19 @@ namespace Godot
/// Note: This results in a multiplication by the inverse of the
/// basis matrix only if it represents a rotation-reflection.
/// </summary>
+ /// <seealso cref="Xform(Vector3)"/>
/// <param name="v">A vector to inversely transform.</param>
/// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
{
return new Vector3
(
- this.Row0[0] * v.x + this.Row1[0] * v.y + this.Row2[0] * v.z,
- this.Row0[1] * v.x + this.Row1[1] * v.y + this.Row2[1] * v.z,
- this.Row0[2] * v.x + this.Row1[2] * v.y + this.Row2[2] * v.z
+ Row0[0] * v.x + Row1[0] * v.y + Row2[0] * v.z,
+ Row0[1] * v.x + Row1[1] * v.y + Row2[1] * v.z,
+ Row0[2] * v.x + Row1[2] * v.y + Row2[2] * v.z
);
}
- /// <summary>
- /// Returns the basis's rotation in the form of a quaternion.
- /// See <see cref="GetEuler()"/> if you need Euler angles, but keep in
- /// mind that quaternions should generally be preferred to Euler angles.
- /// </summary>
- /// <returns>A <see cref="Godot.Quaternion"/> representing the basis's rotation.</returns>
- public Quaternion Quaternion()
- {
- real_t trace = Row0[0] + Row1[1] + Row2[2];
-
- if (trace > 0.0f)
- {
- real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quaternion(
- (Row2[1] - Row1[2]) * inv_s,
- (Row0[2] - Row2[0]) * inv_s,
- (Row1[0] - Row0[1]) * inv_s,
- s * 0.25f
- );
- }
-
- if (Row0[0] > Row1[1] && Row0[0] > Row2[2])
- {
- real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quaternion(
- s * 0.25f,
- (Row0[1] + Row1[0]) * inv_s,
- (Row0[2] + Row2[0]) * inv_s,
- (Row2[1] - Row1[2]) * inv_s
- );
- }
-
- if (Row1[1] > Row2[2])
- {
- real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quaternion(
- (Row0[1] + Row1[0]) * inv_s,
- s * 0.25f,
- (Row1[2] + Row2[1]) * inv_s,
- (Row0[2] - Row2[0]) * inv_s
- );
- }
- else
- {
- real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quaternion(
- (Row0[2] + Row2[0]) * inv_s,
- (Row1[2] + Row2[1]) * inv_s,
- s * 0.25f,
- (Row1[0] - Row0[1]) * inv_s
- );
- }
- }
-
private static readonly Basis[] _orthoBases = {
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
@@ -675,25 +692,25 @@ namespace Godot
/// <summary>
/// The identity basis, with no rotation or scaling applied.
- /// This is used as a replacement for `Basis()` in GDScript.
- /// Do not use `new Basis()` with no arguments in C#, because it sets all values to zero.
+ /// This is used as a replacement for <c>Basis()</c> in GDScript.
+ /// Do not use <c>new Basis()</c> with no arguments in C#, because it sets all values to zero.
/// </summary>
- /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Back)`.</value>
+ /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Up, Vector3.Back)</c>.</value>
public static Basis Identity { get { return _identity; } }
/// <summary>
/// The basis that will flip something along the X axis when used in a transformation.
/// </summary>
- /// <value>Equivalent to `new Basis(Vector3.Left, Vector3.Up, Vector3.Back)`.</value>
+ /// <value>Equivalent to <c>new Basis(Vector3.Left, Vector3.Up, Vector3.Back)</c>.</value>
public static Basis FlipX { get { return _flipX; } }
/// <summary>
/// The basis that will flip something along the Y axis when used in a transformation.
/// </summary>
- /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Down, Vector3.Back)`.</value>
+ /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Down, Vector3.Back)</c>.</value>
public static Basis FlipY { get { return _flipY; } }
/// <summary>
/// The basis that will flip something along the Z axis when used in a transformation.
/// </summary>
- /// <value>Equivalent to `new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)`.</value>
+ /// <value>Equivalent to <c>new Basis(Vector3.Right, Vector3.Up, Vector3.Forward)</c>.</value>
public static Basis FlipZ { get { return _flipZ; } }
/// <summary>
@@ -728,7 +745,7 @@ namespace Godot
/// given in the vector format as (X angle, Y angle, Z angle).
///
/// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which
- /// uses a <see cref="Godot.Quaternion"/> quaternion instead of Euler angles.
+ /// uses a <see cref="Quaternion"/> quaternion instead of Euler angles.
/// </summary>
/// <param name="eulerYXZ">The Euler angles to create the basis from.</param>
public Basis(Vector3 eulerYXZ)
@@ -752,8 +769,8 @@ namespace Godot
}
/// <summary>
- /// Constructs a pure rotation basis matrix, rotated around the given `axis`
- /// by `phi` (in radians). The axis must be a normalized vector.
+ /// Constructs a pure rotation basis matrix, rotated around the given <paramref name="axis"/>
+ /// by <paramref name="phi"/> (in radians). The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
/// <param name="phi">The angle to rotate, in radians.</param>
@@ -810,6 +827,14 @@ namespace Godot
Row2 = new Vector3(xz, yz, zz);
}
+ /// <summary>
+ /// Composes these two basis matrices by multiplying them
+ /// together. This has the effect of transforming the second basis
+ /// (the child) by the first basis (the parent).
+ /// </summary>
+ /// <param name="left">The parent basis.</param>
+ /// <param name="right">The child basis.</param>
+ /// <returns>The composed basis.</returns>
public static Basis operator *(Basis left, Basis right)
{
return new Basis
@@ -820,16 +845,40 @@ namespace Godot
);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the basis matrices are exactly
+ /// equal. Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left basis.</param>
+ /// <param name="right">The right basis.</param>
+ /// <returns>Whether or not the basis matrices are exactly equal.</returns>
public static bool operator ==(Basis left, Basis right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the basis matrices are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left basis.</param>
+ /// <param name="right">The right basis.</param>
+ /// <returns>Whether or not the basis matrices are not equal.</returns>
public static bool operator !=(Basis left, Basis right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the <see cref="Basis"/> is
+ /// exactly equal to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the basis matrix and the object are exactly equal.</returns>
public override bool Equals(object obj)
{
if (obj is Basis)
@@ -840,32 +889,51 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the basis matrices are exactly
+ /// equal. Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other basis.</param>
+ /// <returns>Whether or not the basis matrices are exactly equal.</returns>
public bool Equals(Basis other)
{
return Row0.Equals(other.Row0) && Row1.Equals(other.Row1) && Row2.Equals(other.Row2);
}
/// <summary>
- /// Returns true if this basis and `other` are approximately equal, by running
- /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// Returns <see langword="true"/> if this basis and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
/// </summary>
/// <param name="other">The other basis to compare.</param>
- /// <returns>Whether or not the matrices are approximately equal.</returns>
+ /// <returns>Whether or not the bases are approximately equal.</returns>
public bool IsEqualApprox(Basis other)
{
return Row0.IsEqualApprox(other.Row0) && Row1.IsEqualApprox(other.Row1) && Row2.IsEqualApprox(other.Row2);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Basis"/>.
+ /// </summary>
+ /// <returns>A hash code for this basis.</returns>
public override int GetHashCode()
{
return Row0.GetHashCode() ^ Row1.GetHashCode() ^ Row2.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Basis"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this basis.</returns>
public override string ToString()
{
return $"[X: {x}, Y: {y}, Z: {z}]";
}
+ /// <summary>
+ /// Converts this <see cref="Basis"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this basis.</returns>
public string ToString(string format)
{
return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, Z: {z.ToString(format)}]";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
index c85cc1884c..2722b64e6d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs
@@ -1,19 +1,60 @@
using System;
+using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// Callable is a first class object which can be held in variables and passed to functions.
+ /// It represents a given method in an Object, and is typically used for signal callbacks.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// public void PrintArgs(object ar1, object arg2, object arg3 = null)
+ /// {
+ /// GD.PrintS(arg1, arg2, arg3);
+ /// }
+ ///
+ /// public void Test()
+ /// {
+ /// // This Callable object will call the PrintArgs method defined above.
+ /// Callable callable = new Callable(this, nameof(PrintArgs));
+ /// callable.Call("hello", "world"); // Prints "hello world null".
+ /// callable.Call(Vector2.Up, 42, callable); // Prints "(0, -1) 42 Node(Node.cs)::PrintArgs".
+ /// callable.Call("invalid"); // Invalid call, should have at least 2 arguments.
+ /// }
+ /// </code>
+ /// </example>
public struct Callable
{
private readonly Object _target;
private readonly StringName _method;
private readonly Delegate _delegate;
+ /// <summary>
+ /// Object that contains the method.
+ /// </summary>
public Object Target => _target;
+ /// <summary>
+ /// Name of the method that will be called.
+ /// </summary>
public StringName Method => _method;
+ /// <summary>
+ /// Delegate of the method that will be called.
+ /// </summary>
public Delegate Delegate => _delegate;
+ /// <summary>
+ /// Converts a <see cref="Delegate"/> to a <see cref="Callable"/>.
+ /// </summary>
+ /// <param name="delegate">The delegate to convert.</param>
public static implicit operator Callable(Delegate @delegate) => new Callable(@delegate);
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the method called <paramref name="method"/>
+ /// in the specified <paramref name="target"/>.
+ /// </summary>
+ /// <param name="target">Object that contains the method.</param>
+ /// <param name="method">Name of the method that will be called.</param>
public Callable(Object target, StringName method)
{
_target = target;
@@ -21,11 +62,42 @@ namespace Godot
_delegate = null;
}
+ /// <summary>
+ /// Constructs a new <see cref="Callable"/> for the given <paramref name="delegate"/>.
+ /// </summary>
+ /// <param name="delegate">Delegate method that will be called.</param>
public Callable(Delegate @delegate)
{
_target = null;
_method = null;
_delegate = @delegate;
}
+
+ /// <summary>
+ /// Calls the method represented by this <see cref="Callable"/>.
+ /// Arguments can be passed and should match the method's signature.
+ /// </summary>
+ /// <param name="args">Arguments that will be passed to the method call.</param>
+ /// <returns>The value returned by the method.</returns>
+ public object Call(params object[] args)
+ {
+ return godot_icall_Callable_Call(ref this, args);
+ }
+
+ /// <summary>
+ /// Calls the method represented by this <see cref="Callable"/> in deferred mode, i.e. during the idle frame.
+ /// Arguments can be passed and should match the method's signature.
+ /// </summary>
+ /// <param name="args">Arguments that will be passed to the method call.</param>
+ public void CallDeferred(params object[] args)
+ {
+ godot_icall_Callable_CallDeferred(ref this, args);
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern object godot_icall_Callable_Call(ref Callable callable, object[] args);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void godot_icall_Callable_CallDeferred(ref Callable callable, object[] args);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
index b9a98ba9c7..fc9d40ca48 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs
@@ -7,11 +7,11 @@ namespace Godot
/// A color represented by red, green, blue, and alpha (RGBA) components.
/// The alpha component is often used for transparency.
/// Values are in floating-point and usually range from 0 to 1.
- /// Some properties (such as CanvasItem.modulate) may accept values
+ /// Some properties (such as <see cref="CanvasItem.Modulate"/>) may accept values
/// greater than 1 (overbright or HDR colors).
///
/// If you want to supply values in a range of 0 to 255, you should use
- /// <see cref="Color8"/> and the `r8`/`g8`/`b8`/`a8` properties.
+ /// <see cref="Color8"/> and the <c>r8</c>/<c>g8</c>/<c>b8</c>/<c>a8</c> properties.
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
@@ -127,11 +127,11 @@ namespace Godot
}
else if (g == max)
{
- h = 2 + (b - r) / delta; // Between cyan & yellow
+ h = 2 + ((b - r) / delta); // Between cyan & yellow
}
else
{
- h = 4 + (r - g) / delta; // Between magenta & cyan
+ h = 4 + ((r - g) / delta); // Between magenta & cyan
}
h /= 6.0f;
@@ -173,7 +173,7 @@ namespace Godot
/// <summary>
/// The HSV value (brightness) of this color, on the range 0 to 1.
/// </summary>
- /// <value>Getting is equivalent to using `Max()` on the RGB components. Setting uses <see cref="FromHSV"/>.</value>
+ /// <value>Getting is equivalent to using <see cref="Math.Max(float, float)"/> on the RGB components. Setting uses <see cref="FromHSV"/>.</value>
public float v
{
get
@@ -189,7 +189,12 @@ namespace Godot
/// <summary>
/// Access color components using their index.
/// </summary>
- /// <value>`[0]` is equivalent to `.r`, `[1]` is equivalent to `.g`, `[2]` is equivalent to `.b`, `[3]` is equivalent to `.a`.</value>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="r"/>,
+ /// <c>[1]</c> is equivalent to <see cref="g"/>,
+ /// <c>[2]</c> is equivalent to <see cref="b"/>,
+ /// <c>[3]</c> is equivalent to <see cref="a"/>.
+ /// </value>
public float this[int index]
{
get
@@ -236,30 +241,30 @@ namespace Godot
/// The second color may have a range of alpha values.
/// </summary>
/// <param name="over">The color to blend over.</param>
- /// <returns>This color blended over `over`.</returns>
+ /// <returns>This color blended over <paramref name="over"/>.</returns>
public Color Blend(Color over)
{
Color res;
float sa = 1.0f - over.a;
- res.a = a * sa + over.a;
+ res.a = (a * sa) + over.a;
if (res.a == 0)
{
return new Color(0, 0, 0, 0);
}
- res.r = (r * a * sa + over.r * over.a) / res.a;
- res.g = (g * a * sa + over.g * over.a) / res.a;
- res.b = (b * a * sa + over.b * over.a) / res.a;
+ res.r = ((r * a * sa) + (over.r * over.a)) / res.a;
+ res.g = ((g * a * sa) + (over.g * over.a)) / res.a;
+ res.b = ((b * a * sa) + (over.b * over.a)) / res.a;
return res;
}
/// <summary>
/// Returns a new color with all components clamped between the
- /// components of `min` and `max` using
- /// <see cref="Mathf.Clamp(float, float, float)"/>.
+ /// components of <paramref name="min"/> and <paramref name="max"/>
+ /// using <see cref="Mathf.Clamp(float, float, float)"/>.
/// </summary>
/// <param name="min">The color with minimum allowed values.</param>
/// <param name="max">The color with maximum allowed values.</param>
@@ -286,14 +291,14 @@ namespace Godot
public Color Darkened(float amount)
{
Color res = this;
- res.r = res.r * (1.0f - amount);
- res.g = res.g * (1.0f - amount);
- res.b = res.b * (1.0f - amount);
+ res.r *= 1.0f - amount;
+ res.g *= 1.0f - amount;
+ res.b *= 1.0f - amount;
return res;
}
/// <summary>
- /// Returns the inverted color: `(1 - r, 1 - g, 1 - b, a)`.
+ /// Returns the inverted color: <c>(1 - r, 1 - g, 1 - b, a)</c>.
/// </summary>
/// <returns>The inverted color.</returns>
public Color Inverted()
@@ -315,15 +320,15 @@ namespace Godot
public Color Lightened(float amount)
{
Color res = this;
- res.r = res.r + (1.0f - res.r) * amount;
- res.g = res.g + (1.0f - res.g) * amount;
- res.b = res.b + (1.0f - res.b) * amount;
+ res.r += (1.0f - res.r) * amount;
+ res.g += (1.0f - res.g) * amount;
+ res.b += (1.0f - res.b) * amount;
return res;
}
/// <summary>
/// Returns the result of the linear interpolation between
- /// this color and `to` by amount `weight`.
+ /// this color and <paramref name="to"/> by amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination color for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -341,7 +346,7 @@ namespace Godot
/// <summary>
/// Returns the result of the linear interpolation between
- /// this color and `to` by color amount `weight`.
+ /// this color and <paramref name="to"/> by color amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination color for interpolation.</param>
/// <param name="weight">A color with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -362,7 +367,7 @@ namespace Godot
/// format (each byte represents a color channel).
/// ABGR is the reversed version of the default format.
/// </summary>
- /// <returns>A uint representing this color in ABGR32 format.</returns>
+ /// <returns>A <see langword="uint"/> representing this color in ABGR32 format.</returns>
public uint ToAbgr32()
{
uint c = (byte)Math.Round(a * 255);
@@ -381,7 +386,7 @@ namespace Godot
/// format (each word represents a color channel).
/// ABGR is the reversed version of the default format.
/// </summary>
- /// <returns>A ulong representing this color in ABGR64 format.</returns>
+ /// <returns>A <see langword="ulong"/> representing this color in ABGR64 format.</returns>
public ulong ToAbgr64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -400,7 +405,7 @@ namespace Godot
/// format (each byte represents a color channel).
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
- /// <returns>A uint representing this color in ARGB32 format.</returns>
+ /// <returns>A <see langword="uint"/> representing this color in ARGB32 format.</returns>
public uint ToArgb32()
{
uint c = (byte)Math.Round(a * 255);
@@ -419,7 +424,7 @@ namespace Godot
/// format (each word represents a color channel).
/// ARGB is more compatible with DirectX, but not used much in Godot.
/// </summary>
- /// <returns>A ulong representing this color in ARGB64 format.</returns>
+ /// <returns>A <see langword="ulong"/> representing this color in ARGB64 format.</returns>
public ulong ToArgb64()
{
ulong c = (ushort)Math.Round(a * 65535);
@@ -438,7 +443,7 @@ namespace Godot
/// format (each byte represents a color channel).
/// RGBA is Godot's default and recommended format.
/// </summary>
- /// <returns>A uint representing this color in RGBA32 format.</returns>
+ /// <returns>A <see langword="uint"/> representing this color in RGBA32 format.</returns>
public uint ToRgba32()
{
uint c = (byte)Math.Round(r * 255);
@@ -457,7 +462,7 @@ namespace Godot
/// format (each word represents a color channel).
/// RGBA is Godot's default and recommended format.
/// </summary>
- /// <returns>A ulong representing this color in RGBA64 format.</returns>
+ /// <returns>A <see langword="ulong"/> representing this color in RGBA64 format.</returns>
public ulong ToRgba64()
{
ulong c = (ushort)Math.Round(r * 65535);
@@ -474,11 +479,13 @@ namespace Godot
/// <summary>
/// Returns the color's HTML hexadecimal color string in RGBA format.
/// </summary>
- /// <param name="includeAlpha">Whether or not to include alpha. If false, the color is RGB instead of RGBA.</param>
+ /// <param name="includeAlpha">
+ /// Whether or not to include alpha. If <see langword="false"/>, the color is RGB instead of RGBA.
+ /// </param>
/// <returns>A string for the HTML hexadecimal representation of this color.</returns>
public string ToHTML(bool includeAlpha = true)
{
- var txt = string.Empty;
+ string txt = string.Empty;
txt += ToHex32(r);
txt += ToHex32(g);
@@ -493,7 +500,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from RGBA values, typically on the range of 0 to 1.
+ /// Constructs a <see cref="Color"/> from RGBA values, typically on the range of 0 to 1.
/// </summary>
/// <param name="r">The color's red component, typically on the range of 0 to 1.</param>
/// <param name="g">The color's green component, typically on the range of 0 to 1.</param>
@@ -508,7 +515,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from an existing color and an alpha value.
+ /// Constructs a <see cref="Color"/> from an existing color and an alpha value.
/// </summary>
/// <param name="c">The color to construct from. Only its RGB values are used.</param>
/// <param name="a">The color's alpha (transparency) value, typically on the range of 0 to 1. Default: 1.</param>
@@ -521,10 +528,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from an unsigned 32-bit integer in RGBA format
+ /// Constructs a <see cref="Color"/> from an unsigned 32-bit integer in RGBA format
/// (each byte represents a color channel).
/// </summary>
- /// <param name="rgba">The uint representing the color.</param>
+ /// <param name="rgba">The <see langword="uint"/> representing the color.</param>
public Color(uint rgba)
{
a = (rgba & 0xFF) / 255.0f;
@@ -537,10 +544,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from an unsigned 64-bit integer in RGBA format
+ /// Constructs a <see cref="Color"/> from an unsigned 64-bit integer in RGBA format
/// (each word represents a color channel).
/// </summary>
- /// <param name="rgba">The ulong representing the color.</param>
+ /// <param name="rgba">The <see langword="ulong"/> representing the color.</param>
public Color(ulong rgba)
{
a = (rgba & 0xFFFF) / 65535.0f;
@@ -553,9 +560,9 @@ namespace Godot
}
/// <summary>
- /// Constructs a color either from an HTML color code or from a
- /// standardized color name. Supported
- /// color names are the same as the <see cref="Colors"/> constants.
+ /// Constructs a <see cref="Color"/> either from an HTML color code or from a
+ /// standardized color name. Supported color names are the same as the
+ /// <see cref="Colors"/> constants.
/// </summary>
/// <param name="code">The HTML color code or color name to construct from.</param>
public Color(string code)
@@ -571,8 +578,8 @@ namespace Godot
}
/// <summary>
- /// Constructs a color either from an HTML color code or from a
- /// standardized color name, with `alpha` on the range of 0 to 1. Supported
+ /// Constructs a <see cref="Color"/> either from an HTML color code or from a
+ /// standardized color name, with <paramref name="alpha"/> on the range of 0 to 1. Supported
/// color names are the same as the <see cref="Colors"/> constants.
/// </summary>
/// <param name="code">The HTML color code or color name to construct from.</param>
@@ -584,9 +591,12 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from the HTML hexadecimal color string in RGBA format.
+ /// Constructs a <see cref="Color"/> from the HTML hexadecimal color string in RGBA format.
/// </summary>
/// <param name="rgba">A string for the HTML hexadecimal representation of this color.</param>
+ /// <exception name="ArgumentOutOfRangeException">
+ /// Thrown when the given <paramref name="rgba"/> color code is invalid.
+ /// </exception>
private static Color FromHTML(string rgba)
{
Color c;
@@ -627,7 +637,8 @@ namespace Godot
}
else
{
- throw new ArgumentOutOfRangeException("Invalid color code. Length is " + rgba.Length + " but a length of 6 or 8 is expected: " + rgba);
+ throw new ArgumentOutOfRangeException(
+ $"Invalid color code. Length is {rgba.Length}, but a length of 6 or 8 is expected: {rgba}");
}
c.a = 1.0f;
@@ -697,11 +708,11 @@ namespace Godot
/// <returns>The constructed color.</returns>
private static Color Named(string name)
{
- name = name.Replace(" ", String.Empty);
- name = name.Replace("-", String.Empty);
- name = name.Replace("_", String.Empty);
- name = name.Replace("'", String.Empty);
- name = name.Replace(".", String.Empty);
+ name = name.Replace(" ", string.Empty);
+ name = name.Replace("-", string.Empty);
+ name = name.Replace("_", string.Empty);
+ name = name.Replace("'", string.Empty);
+ name = name.Replace(".", string.Empty);
name = name.ToUpper();
if (!Colors.namedColors.ContainsKey(name))
@@ -715,7 +726,7 @@ namespace Godot
/// <summary>
/// Constructs a color from an HSV profile, with values on the
/// range of 0 to 1. This is equivalent to using each of
- /// the `h`/`s`/`v` properties, but much more efficient.
+ /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient.
/// </summary>
/// <param name="hue">The HSV hue, typically on the range of 0 to 1.</param>
/// <param name="saturation">The HSV saturation, typically on the range of 0 to 1.</param>
@@ -739,8 +750,8 @@ namespace Godot
f = hue - i;
p = value * (1 - saturation);
- q = value * (1 - saturation * f);
- t = value * (1 - saturation * (1 - f));
+ q = value * (1 - (saturation * f));
+ t = value * (1 - (saturation * (1 - f)));
switch (i)
{
@@ -761,7 +772,7 @@ namespace Godot
/// <summary>
/// Converts a color to HSV values. This is equivalent to using each of
- /// the `h`/`s`/`v` properties, but much more efficient.
+ /// the <c>h</c>/<c>s</c>/<c>v</c> properties, but much more efficient.
/// </summary>
/// <param name="hue">Output parameter for the HSV hue.</param>
/// <param name="saturation">Output parameter for the HSV saturation.</param>
@@ -785,22 +796,24 @@ namespace Godot
}
else if (g == max)
{
- hue = 2 + (b - r) / delta; // Between cyan & yellow
+ hue = 2 + ((b - r) / delta); // Between cyan & yellow
}
else
{
- hue = 4 + (r - g) / delta; // Between magenta & cyan
+ hue = 4 + ((r - g) / delta); // Between magenta & cyan
}
hue /= 6.0f;
if (hue < 0)
- {
hue += 1.0f;
- }
}
- saturation = max == 0 ? 0 : 1f - 1f * min / max;
+ if (max == 0)
+ saturation = 0;
+ else
+ saturation = 1 - (min / max);
+
value = max;
}
@@ -865,6 +878,13 @@ namespace Godot
return true;
}
+ /// <summary>
+ /// Adds each component of the <see cref="Color"/>
+ /// with the components of the given <see cref="Color"/>.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>The added color.</returns>
public static Color operator +(Color left, Color right)
{
left.r += right.r;
@@ -874,6 +894,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Subtracts each component of the <see cref="Color"/>
+ /// by the components of the given <see cref="Color"/>.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>The subtracted color.</returns>
public static Color operator -(Color left, Color right)
{
left.r -= right.r;
@@ -883,11 +910,25 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Inverts the given color. This is equivalent to
+ /// <c>Colors.White - c</c> or
+ /// <c>new Color(1 - c.r, 1 - c.g, 1 - c.b, 1 - c.a)</c>.
+ /// </summary>
+ /// <param name="color">The color to invert.</param>
+ /// <returns>The inverted color</returns>
public static Color operator -(Color color)
{
return Colors.White - color;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Color"/>
+ /// by the given <see langword="float"/>.
+ /// </summary>
+ /// <param name="color">The color to multiply.</param>
+ /// <param name="scale">The value to multiply by.</param>
+ /// <returns>The multiplied color.</returns>
public static Color operator *(Color color, float scale)
{
color.r *= scale;
@@ -897,6 +938,13 @@ namespace Godot
return color;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Color"/>
+ /// by the given <see langword="float"/>.
+ /// </summary>
+ /// <param name="scale">The value to multiply by.</param>
+ /// <param name="color">The color to multiply.</param>
+ /// <returns>The multiplied color.</returns>
public static Color operator *(float scale, Color color)
{
color.r *= scale;
@@ -906,6 +954,13 @@ namespace Godot
return color;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Color"/>
+ /// by the components of the given <see cref="Color"/>.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>The multiplied color.</returns>
public static Color operator *(Color left, Color right)
{
left.r *= right.r;
@@ -915,6 +970,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Color"/>
+ /// by the given <see langword="float"/>.
+ /// </summary>
+ /// <param name="color">The dividend vector.</param>
+ /// <param name="scale">The divisor value.</param>
+ /// <returns>The divided color.</returns>
public static Color operator /(Color color, float scale)
{
color.r /= scale;
@@ -924,6 +986,13 @@ namespace Godot
return color;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Color"/>
+ /// by the components of the given <see cref="Color"/>.
+ /// </summary>
+ /// <param name="left">The dividend color.</param>
+ /// <param name="right">The divisor color.</param>
+ /// <returns>The divided color.</returns>
public static Color operator /(Color left, Color right)
{
left.r /= right.r;
@@ -933,23 +1002,51 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the colors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the colors are equal.</returns>
public static bool operator ==(Color left, Color right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the colors are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the colors are equal.</returns>
public static bool operator !=(Color left, Color right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Compares two <see cref="Color"/>s by first checking if
+ /// the red value of the <paramref name="left"/> color is less than
+ /// the red value of the <paramref name="right"/> color.
+ /// If the red values are exactly equal, then it repeats this check
+ /// with the green values of the two colors, then with the blue values,
+ /// and then with the alpha value.
+ /// This operator is useful for sorting colors.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Color left, Color right)
{
- if (Mathf.IsEqualApprox(left.r, right.r))
+ if (left.r == right.r)
{
- if (Mathf.IsEqualApprox(left.g, right.g))
+ if (left.g == right.g)
{
- if (Mathf.IsEqualApprox(left.b, right.b))
+ if (left.b == right.b)
{
return left.a < right.a;
}
@@ -960,13 +1057,25 @@ namespace Godot
return left.r < right.r;
}
+ /// <summary>
+ /// Compares two <see cref="Color"/>s by first checking if
+ /// the red value of the <paramref name="left"/> color is greater than
+ /// the red value of the <paramref name="right"/> color.
+ /// If the red values are exactly equal, then it repeats this check
+ /// with the green values of the two colors, then with the blue values,
+ /// and then with the alpha value.
+ /// This operator is useful for sorting colors.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Color left, Color right)
{
- if (Mathf.IsEqualApprox(left.r, right.r))
+ if (left.r == right.r)
{
- if (Mathf.IsEqualApprox(left.g, right.g))
+ if (left.g == right.g)
{
- if (Mathf.IsEqualApprox(left.b, right.b))
+ if (left.b == right.b)
{
return left.a > right.a;
}
@@ -977,6 +1086,69 @@ namespace Godot
return left.r > right.r;
}
+ /// <summary>
+ /// Compares two <see cref="Color"/>s by first checking if
+ /// the red value of the <paramref name="left"/> color is less than
+ /// or equal to the red value of the <paramref name="right"/> color.
+ /// If the red values are exactly equal, then it repeats this check
+ /// with the green values of the two colors, then with the blue values,
+ /// and then with the alpha value.
+ /// This operator is useful for sorting colors.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
+ public static bool operator <=(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ {
+ return left.a <= right.a;
+ }
+ return left.b < right.b;
+ }
+ return left.g < right.g;
+ }
+ return left.r < right.r;
+ }
+
+ /// <summary>
+ /// Compares two <see cref="Color"/>s by first checking if
+ /// the red value of the <paramref name="left"/> color is greater than
+ /// or equal to the red value of the <paramref name="right"/> color.
+ /// If the red values are exactly equal, then it repeats this check
+ /// with the green values of the two colors, then with the blue values,
+ /// and then with the alpha value.
+ /// This operator is useful for sorting colors.
+ /// </summary>
+ /// <param name="left">The left color.</param>
+ /// <param name="right">The right color.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
+ public static bool operator >=(Color left, Color right)
+ {
+ if (left.r == right.r)
+ {
+ if (left.g == right.g)
+ {
+ if (left.b == right.b)
+ {
+ return left.a >= right.a;
+ }
+ return left.b > right.b;
+ }
+ return left.g > right.g;
+ }
+ return left.r > right.r;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if this color and <paramref name="obj"/> are equal.
+ /// </summary>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns>Whether or not the color and the other object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Color)
@@ -987,14 +1159,21 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the colors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other color.</param>
+ /// <returns>Whether or not the colors are equal.</returns>
public bool Equals(Color other)
{
return r == other.r && g == other.g && b == other.b && a == other.a;
}
/// <summary>
- /// Returns true if this color and `other` are approximately equal, by running
- /// <see cref="Godot.Mathf.IsEqualApprox(float, float)"/> on each component.
+ /// Returns <see langword="true"/> if this color and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Mathf.IsEqualApprox(float, float)"/> on each component.
/// </summary>
/// <param name="other">The other color to compare.</param>
/// <returns>Whether or not the colors are approximately equal.</returns>
@@ -1003,16 +1182,28 @@ namespace Godot
return Mathf.IsEqualApprox(r, other.r) && Mathf.IsEqualApprox(g, other.g) && Mathf.IsEqualApprox(b, other.b) && Mathf.IsEqualApprox(a, other.a);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Color"/>.
+ /// </summary>
+ /// <returns>A hash code for this color.</returns>
public override int GetHashCode()
{
return r.GetHashCode() ^ g.GetHashCode() ^ b.GetHashCode() ^ a.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Color"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this color.</returns>
public override string ToString()
{
return $"({r}, {g}, {b}, {a})";
}
+ /// <summary>
+ /// Converts this <see cref="Color"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this color.</returns>
public string ToString(string format)
{
return $"({r.ToString(format)}, {g.ToString(format)}, {b.ToString(format)}, {a.ToString(format)})";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
index d64c8b563e..68c821b447 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -158,6 +158,7 @@ namespace Godot
{"YELLOWGREEN", new Color(0.60f, 0.80f, 0.20f)},
};
+#pragma warning disable CS1591 // Disable warning: "Missing XML comment for publicly visible type or member"
public static Color AliceBlue { get { return namedColors["ALICEBLUE"]; } }
public static Color AntiqueWhite { get { return namedColors["ANTIQUEWHITE"]; } }
public static Color Aqua { get { return namedColors["AQUA"]; } }
@@ -304,5 +305,6 @@ namespace Godot
public static Color WhiteSmoke { get { return namedColors["WHITESMOKE"]; } }
public static Color Yellow { get { return namedColors["YELLOW"]; } }
public static Color YellowGreen { get { return namedColors["YELLOWGREEN"]; } }
+#pragma warning restore CS1591
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 61a34bfc87..75240b0c09 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis;
namespace Godot.Collections
{
- class DictionarySafeHandle : SafeHandle
+ internal class DictionarySafeHandle : SafeHandle
{
public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
@@ -31,19 +31,17 @@ namespace Godot.Collections
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine.
/// </summary>
- public class Dictionary :
- IDictionary,
- IDisposable
+ public class Dictionary : IDictionary, IDisposable
{
- DictionarySafeHandle safeHandle;
- bool disposed = false;
+ private DictionarySafeHandle _safeHandle;
+ private bool _disposed = false;
/// <summary>
/// Constructs a new empty <see cref="Dictionary"/>.
/// </summary>
public Dictionary()
{
- safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
+ _safeHandle = new DictionarySafeHandle(godot_icall_Dictionary_Ctor());
}
/// <summary>
@@ -62,20 +60,20 @@ namespace Godot.Collections
internal Dictionary(DictionarySafeHandle handle)
{
- safeHandle = handle;
+ _safeHandle = handle;
}
internal Dictionary(IntPtr handle)
{
- safeHandle = new DictionarySafeHandle(handle);
+ _safeHandle = new DictionarySafeHandle(handle);
}
internal IntPtr GetPtr()
{
- if (disposed)
+ if (_disposed)
throw new ObjectDisposedException(GetType().FullName);
- return safeHandle.DangerousGetHandle();
+ return _safeHandle.DangerousGetHandle();
}
/// <summary>
@@ -83,16 +81,16 @@ namespace Godot.Collections
/// </summary>
public void Dispose()
{
- if (disposed)
+ if (_disposed)
return;
- if (safeHandle != null)
+ if (_safeHandle != null)
{
- safeHandle.Dispose();
- safeHandle = null;
+ _safeHandle.Dispose();
+ _safeHandle = null;
}
- disposed = true;
+ _disposed = true;
}
/// <summary>
@@ -230,17 +228,17 @@ namespace Godot.Collections
private class DictionaryEnumerator : IDictionaryEnumerator
{
- private readonly Dictionary dictionary;
- private readonly int count;
- private int index = -1;
- private bool dirty = true;
+ private readonly Dictionary _dictionary;
+ private readonly int _count;
+ private int _index = -1;
+ private bool _dirty = true;
- private DictionaryEntry entry;
+ private DictionaryEntry _entry;
public DictionaryEnumerator(Dictionary dictionary)
{
- this.dictionary = dictionary;
- count = dictionary.Count;
+ _dictionary = dictionary;
+ _count = dictionary.Count;
}
public object Current => Entry;
@@ -249,19 +247,19 @@ namespace Godot.Collections
{
get
{
- if (dirty)
+ if (_dirty)
{
UpdateEntry();
}
- return entry;
+ return _entry;
}
}
private void UpdateEntry()
{
- dirty = false;
- godot_icall_Dictionary_KeyValuePairAt(dictionary.GetPtr(), index, out object key, out object value);
- entry = new DictionaryEntry(key, value);
+ _dirty = false;
+ godot_icall_Dictionary_KeyValuePairAt(_dictionary.GetPtr(), _index, out object key, out object value);
+ _entry = new DictionaryEntry(key, value);
}
public object Key => Entry.Key;
@@ -270,15 +268,15 @@ namespace Godot.Collections
public bool MoveNext()
{
- index++;
- dirty = true;
- return index < count;
+ _index++;
+ _dirty = true;
+ return _index < _count;
}
public void Reset()
{
- index = -1;
- dirty = true;
+ _index = -1;
+ _dirty = true;
}
}
@@ -292,67 +290,67 @@ namespace Godot.Collections
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Dictionary_Ctor();
+ internal static extern IntPtr godot_icall_Dictionary_Ctor();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_Dtor(IntPtr ptr);
+ internal static extern void godot_icall_Dictionary_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
+ internal static extern object godot_icall_Dictionary_GetValue(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
+ internal static extern object godot_icall_Dictionary_GetValue_Generic(IntPtr ptr, object key, int valTypeEncoding, IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
+ internal static extern void godot_icall_Dictionary_SetValue(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
+ internal static extern IntPtr godot_icall_Dictionary_Keys(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
+ internal static extern IntPtr godot_icall_Dictionary_Values(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Dictionary_Count(IntPtr ptr);
+ internal static extern int godot_icall_Dictionary_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
+ internal static extern int godot_icall_Dictionary_KeyValuePairs(IntPtr ptr, out IntPtr keys, out IntPtr values);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
+ internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
+ internal static extern void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_Clear(IntPtr ptr);
+ internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
+ internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
+ internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
+ internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
+ internal static extern bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
+ internal static extern bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
+ internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
+ internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
+ internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr);
+ internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr);
}
/// <summary>
@@ -363,10 +361,9 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
- public class Dictionary<TKey, TValue> :
- IDictionary<TKey, TValue>
+ public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
- private readonly Dictionary objectDict;
+ private readonly Dictionary _objectDict;
internal static int valTypeEncoding;
internal static IntPtr valTypeClass;
@@ -381,7 +378,7 @@ namespace Godot.Collections
/// </summary>
public Dictionary()
{
- objectDict = new Dictionary();
+ _objectDict = new Dictionary();
}
/// <summary>
@@ -391,7 +388,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
- objectDict = new Dictionary();
+ _objectDict = new Dictionary();
if (dictionary == null)
throw new NullReferenceException($"Parameter '{nameof(dictionary)} cannot be null.'");
@@ -413,17 +410,17 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
- objectDict = dictionary;
+ _objectDict = dictionary;
}
internal Dictionary(IntPtr handle)
{
- objectDict = new Dictionary(handle);
+ _objectDict = new Dictionary(handle);
}
internal Dictionary(DictionarySafeHandle handle)
{
- objectDict = new Dictionary(handle);
+ _objectDict = new Dictionary(handle);
}
/// <summary>
@@ -432,12 +429,12 @@ namespace Godot.Collections
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
- return from.objectDict;
+ return from._objectDict;
}
internal IntPtr GetPtr()
{
- return objectDict.GetPtr();
+ return _objectDict.GetPtr();
}
/// <summary>
@@ -447,7 +444,7 @@ namespace Godot.Collections
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
- return new Dictionary<TKey, TValue>(objectDict.Duplicate(deep));
+ return new Dictionary<TKey, TValue>(_objectDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
@@ -458,8 +455,8 @@ namespace Godot.Collections
/// <value>The value at the given <paramref name="key"/>.</value>
public TValue this[TKey key]
{
- get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
- set { objectDict[key] = value; }
+ get { return (TValue)Dictionary.godot_icall_Dictionary_GetValue_Generic(_objectDict.GetPtr(), key, valTypeEncoding, valTypeClass); }
+ set { _objectDict[key] = value; }
}
/// <summary>
@@ -469,7 +466,7 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(objectDict.GetPtr());
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Keys(_objectDict.GetPtr());
return new Array<TKey>(new ArraySafeHandle(handle));
}
}
@@ -481,7 +478,7 @@ namespace Godot.Collections
{
get
{
- IntPtr handle = Dictionary.godot_icall_Dictionary_Values(objectDict.GetPtr());
+ IntPtr handle = Dictionary.godot_icall_Dictionary_Values(_objectDict.GetPtr());
return new Array<TValue>(new ArraySafeHandle(handle));
}
}
@@ -500,7 +497,7 @@ namespace Godot.Collections
/// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
- objectDict.Add(key, value);
+ _objectDict.Add(key, value);
}
/// <summary>
@@ -510,7 +507,7 @@ namespace Godot.Collections
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(TKey key)
{
- return objectDict.Contains(key);
+ return _objectDict.Contains(key);
}
/// <summary>
@@ -544,14 +541,14 @@ namespace Godot.Collections
/// <returns>The number of elements.</returns>
public int Count
{
- get { return objectDict.Count; }
+ get { return _objectDict.Count; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
- objectDict.Add(item.Key, item.Value);
+ _objectDict.Add(item.Key, item.Value);
}
/// <summary>
@@ -559,12 +556,12 @@ namespace Godot.Collections
/// </summary>
public void Clear()
{
- objectDict.Clear();
+ _objectDict.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
- return objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
+ return _objectDict.Contains(new KeyValuePair<object, object>(item.Key, item.Value));
}
/// <summary>
@@ -622,6 +619,6 @@ namespace Godot.Collections
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
- public override string ToString() => objectDict.ToString();
+ public override string ToString() => _objectDict.ToString();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
index 0c21bcaa3f..26d5f9c796 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
@@ -7,20 +7,20 @@ using System.Runtime.CompilerServices;
namespace Godot
{
/// <summary>
- /// Represents an <see cref="Godot.Object"/> whose members can be dynamically accessed at runtime through the Variant API.
+ /// Represents an <see cref="Object"/> whose members can be dynamically accessed at runtime through the Variant API.
/// </summary>
/// <remarks>
/// <para>
- /// The <see cref="Godot.DynamicGodotObject"/> class enables access to the Variant
- /// members of a <see cref="Godot.Object"/> instance at runtime.
+ /// The <see cref="DynamicGodotObject"/> class enables access to the Variant
+ /// members of a <see cref="Object"/> instance at runtime.
/// </para>
/// <para>
/// This allows accessing the class members using their original names in the engine as well as the members from the
- /// script attached to the <see cref="Godot.Object"/>, regardless of the scripting language it was written in.
+ /// script attached to the <see cref="Object"/>, regardless of the scripting language it was written in.
/// </para>
/// </remarks>
/// <example>
- /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Godot.Object"/>.
+ /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the engine members of a <see cref="Object"/>.
/// <code>
/// dynamic sprite = GetNode("Sprite2D").DynamicGodotObject;
/// sprite.add_child(this);
@@ -30,7 +30,7 @@ namespace Godot
/// </code>
/// </example>
/// <example>
- /// This sample shows how to use <see cref="Godot.DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Godot.Object"/>.
+ /// This sample shows how to use <see cref="DynamicGodotObject"/> to dynamically access the members of the script attached to a <see cref="Object"/>.
/// <code>
/// dynamic childNode = GetNode("ChildNode").DynamicGodotObject;
///
@@ -54,32 +54,34 @@ namespace Godot
public class DynamicGodotObject : DynamicObject
{
/// <summary>
- /// Gets the <see cref="Godot.Object"/> associated with this <see cref="Godot.DynamicGodotObject"/>.
+ /// Gets the <see cref="Object"/> associated with this <see cref="DynamicGodotObject"/>.
/// </summary>
public Object Value { get; }
/// <summary>
- /// Initializes a new instance of the <see cref="Godot.DynamicGodotObject"/> class.
+ /// Initializes a new instance of the <see cref="DynamicGodotObject"/> class.
/// </summary>
/// <param name="godotObject">
- /// The <see cref="Godot.Object"/> that will be associated with this <see cref="Godot.DynamicGodotObject"/>.
+ /// The <see cref="Object"/> that will be associated with this <see cref="DynamicGodotObject"/>.
/// </param>
- /// <exception cref="System.ArgumentNullException">
- /// Thrown when the <paramref name="godotObject"/> parameter is null.
+ /// <exception cref="ArgumentNullException">
+ /// Thrown when the <paramref name="godotObject"/> parameter is <see langword="null"/>.
/// </exception>
public DynamicGodotObject(Object godotObject)
{
if (godotObject == null)
throw new ArgumentNullException(nameof(godotObject));
- this.Value = godotObject;
+ Value = godotObject;
}
+ /// <inheritdoc/>
public override IEnumerable<string> GetDynamicMemberNames()
{
return godot_icall_DynamicGodotObject_SetMemberList(Object.GetPtr(Value));
}
+ /// <inheritdoc/>
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
{
switch (binder.Operation)
@@ -121,6 +123,7 @@ namespace Godot
return base.TryBinaryOperation(binder, arg, out result);
}
+ /// <inheritdoc/>
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(Object))
@@ -139,6 +142,7 @@ namespace Godot
return base.TryConvert(binder, out result);
}
+ /// <inheritdoc/>
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1)
@@ -152,16 +156,19 @@ namespace Godot
return base.TryGetIndex(binder, indexes, out result);
}
+ /// <inheritdoc/>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return godot_icall_DynamicGodotObject_GetMember(Object.GetPtr(Value), binder.Name, out result);
}
+ /// <inheritdoc/>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
return godot_icall_DynamicGodotObject_InvokeMember(Object.GetPtr(Value), binder.Name, args, out result);
}
+ /// <inheritdoc/>
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
if (indexes.Length == 1)
@@ -175,22 +182,23 @@ namespace Godot
return base.TrySetIndex(binder, indexes, value);
}
+ /// <inheritdoc/>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return godot_icall_DynamicGodotObject_SetMember(Object.GetPtr(Value), binder.Name, value);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
+ internal static extern string[] godot_icall_DynamicGodotObject_SetMemberList(IntPtr godotObject);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
+ internal static extern bool godot_icall_DynamicGodotObject_InvokeMember(IntPtr godotObject, string name, object[] args, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
+ internal static extern bool godot_icall_DynamicGodotObject_GetMember(IntPtr godotObject, string name, out object result);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
+ internal static extern bool godot_icall_DynamicGodotObject_SetMember(IntPtr godotObject, string name, object value);
#region We don't override these methods
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
index 5d16260f5d..1dc21b6303 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/NodeExtensions.cs
@@ -1,42 +1,192 @@
+using System;
+
namespace Godot
{
public partial class Node
{
+ /// <summary>
+ /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
+ /// the current node) or an absolute path (in the scene tree) to a node. If the path
+ /// does not exist, a <see langword="null"/> instance is returned and an error
+ /// is logged. Attempts to access methods on the return value will result in an
+ /// "Attempt to call &lt;method&gt; on a null instance." error.
+ /// Note: Fetching absolute paths only works when the node is inside the scene tree
+ /// (see <see cref="IsInsideTree"/>).
+ /// </summary>
+ /// <example>
+ /// Example: Assume your current node is Character and the following tree:
+ /// <code>
+ /// /root
+ /// /root/Character
+ /// /root/Character/Sword
+ /// /root/Character/Backpack/Dagger
+ /// /root/MyGame
+ /// /root/Swamp/Alligator
+ /// /root/Swamp/Mosquito
+ /// /root/Swamp/Goblin
+ /// </code>
+ /// Possible paths are:
+ /// <code>
+ /// GetNode("Sword");
+ /// GetNode("Backpack/Dagger");
+ /// GetNode("../Swamp/Alligator");
+ /// GetNode("/root/MyGame");
+ /// </code>
+ /// </example>
+ /// <seealso cref="GetNodeOrNull{T}(NodePath)"/>
+ /// <param name="path">The path to the node to fetch.</param>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The <see cref="Node"/> at the given <paramref name="path"/>.
+ /// </returns>
public T GetNode<T>(NodePath path) where T : class
{
return (T)(object)GetNode(path);
}
+ /// <summary>
+ /// Fetches a node. The <see cref="NodePath"/> can be either a relative path (from
+ /// the current node) or an absolute path (in the scene tree) to a node. If the path
+ /// does not exist, a <see langword="null"/> instance is returned and an error
+ /// is logged. Attempts to access methods on the return value will result in an
+ /// "Attempt to call &lt;method&gt; on a null instance." error.
+ /// Note: Fetching absolute paths only works when the node is inside the scene tree
+ /// (see <see cref="IsInsideTree"/>).
+ /// </summary>
+ /// <example>
+ /// Example: Assume your current node is Character and the following tree:
+ /// <code>
+ /// /root
+ /// /root/Character
+ /// /root/Character/Sword
+ /// /root/Character/Backpack/Dagger
+ /// /root/MyGame
+ /// /root/Swamp/Alligator
+ /// /root/Swamp/Mosquito
+ /// /root/Swamp/Goblin
+ /// </code>
+ /// Possible paths are:
+ /// <code>
+ /// GetNode("Sword");
+ /// GetNode("Backpack/Dagger");
+ /// GetNode("../Swamp/Alligator");
+ /// GetNode("/root/MyGame");
+ /// </code>
+ /// </example>
+ /// <seealso cref="GetNode{T}(NodePath)"/>
+ /// <param name="path">The path to the node to fetch.</param>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The <see cref="Node"/> at the given <paramref name="path"/>, or <see langword="null"/> if not found.
+ /// </returns>
public T GetNodeOrNull<T>(NodePath path) where T : class
{
return GetNodeOrNull(path) as T;
}
+ /// <summary>
+ /// Returns a child node by its index (see <see cref="GetChildCount"/>).
+ /// This method is often used for iterating all children of a node.
+ /// Negative indices access the children from the last one.
+ /// To access a child node via its name, use <see cref="GetNode"/>.
+ /// </summary>
+ /// <seealso cref="GetChildOrNull{T}(int)"/>
+ /// <param name="idx">Child index.</param>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The child <see cref="Node"/> at the given index <paramref name="idx"/>.
+ /// </returns>
public T GetChild<T>(int idx) where T : class
{
return (T)(object)GetChild(idx);
}
+ /// <summary>
+ /// Returns a child node by its index (see <see cref="GetChildCount"/>).
+ /// This method is often used for iterating all children of a node.
+ /// Negative indices access the children from the last one.
+ /// To access a child node via its name, use <see cref="GetNode"/>.
+ /// </summary>
+ /// <seealso cref="GetChild{T}(int)"/>
+ /// <param name="idx">Child index.</param>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The child <see cref="Node"/> at the given index <paramref name="idx"/>, or <see langword="null"/> if not found.
+ /// </returns>
public T GetChildOrNull<T>(int idx) where T : class
{
return GetChild(idx) as T;
}
+ /// <summary>
+ /// The node owner. A node can have any other node as owner (as long as it is
+ /// a valid parent, grandparent, etc. ascending in the tree). When saving a
+ /// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved
+ /// with it. This allows for the creation of complex <see cref="SceneTree"/>s,
+ /// with instancing and subinstancing.
+ /// </summary>
+ /// <seealso cref="GetOwnerOrNull{T}"/>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The owner <see cref="Node"/>.
+ /// </returns>
public T GetOwner<T>() where T : class
{
return (T)(object)Owner;
}
+ /// <summary>
+ /// The node owner. A node can have any other node as owner (as long as it is
+ /// a valid parent, grandparent, etc. ascending in the tree). When saving a
+ /// node (using <see cref="PackedScene"/>), all the nodes it owns will be saved
+ /// with it. This allows for the creation of complex <see cref="SceneTree"/>s,
+ /// with instancing and subinstancing.
+ /// </summary>
+ /// <seealso cref="GetOwner{T}"/>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The owner <see cref="Node"/>, or <see langword="null"/> if there is no owner.
+ /// </returns>
public T GetOwnerOrNull<T>() where T : class
{
return Owner as T;
}
+ /// <summary>
+ /// Returns the parent node of the current node, or a <see langword="null"/> instance
+ /// if the node lacks a parent.
+ /// </summary>
+ /// <seealso cref="GetParentOrNull{T}"/>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the fetched node can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The parent <see cref="Node"/>.
+ /// </returns>
public T GetParent<T>() where T : class
{
return (T)(object)GetParent();
}
+ /// <summary>
+ /// Returns the parent node of the current node, or a <see langword="null"/> instance
+ /// if the node lacks a parent.
+ /// </summary>
+ /// <seealso cref="GetParent{T}"/>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>
+ /// The parent <see cref="Node"/>, or <see langword="null"/> if the node has no parent.
+ /// </returns>
public T GetParentOrNull<T>() where T : class
{
return GetParent() as T;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
index 9ef0959750..691fd85964 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ObjectExtensions.cs
@@ -5,17 +5,37 @@ namespace Godot
{
public partial class Object
{
+ /// <summary>
+ /// Returns whether <paramref name="instance"/> is a valid object
+ /// (e.g. has not been deleted from memory).
+ /// </summary>
+ /// <param name="instance">The instance to check.</param>
+ /// <returns>If the instance is a valid object.</returns>
public static bool IsInstanceValid(Object instance)
{
return instance != null && instance.NativeInstance != IntPtr.Zero;
}
+ /// <summary>
+ /// Returns a weak reference to an object, or <see langword="null"/>
+ /// if the argument is invalid.
+ /// A weak reference to an object is not enough to keep the object alive:
+ /// when the only remaining references to a referent are weak references,
+ /// garbage collection is free to destroy the referent and reuse its memory
+ /// for something else. However, until the object is actually destroyed the
+ /// weak reference may return the object even if there are no strong references
+ /// to it.
+ /// </summary>
+ /// <param name="obj">The object.</param>
+ /// <returns>
+ /// The <see cref="WeakRef"/> reference to the object or <see langword="null"/>.
+ /// </returns>
public static WeakRef WeakRef(Object obj)
{
- return godot_icall_Object_weakref(Object.GetPtr(obj));
+ return godot_icall_Object_weakref(GetPtr(obj));
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static WeakRef godot_icall_Object_weakref(IntPtr obj);
+ internal static extern WeakRef godot_icall_Object_weakref(IntPtr obj);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
index 214bbf5179..435b59d5f3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
@@ -1,3 +1,5 @@
+using System;
+
namespace Godot
{
public partial class PackedScene
@@ -5,20 +7,27 @@ namespace Godot
/// <summary>
/// Instantiates the scene's node hierarchy, erroring on failure.
/// Triggers child scene instantiation(s). Triggers a
- /// `Node.NotificationInstanced` notification on the root node.
+ /// <see cref="Node.NotificationInstanced"/> notification on the root node.
/// </summary>
- /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam>
+ /// <seealso cref="InstantiateOrNull{T}(GenEditState)"/>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the instantiated node can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>The instantiated scene.</returns>
public T Instantiate<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
{
return (T)(object)Instantiate(editState);
}
/// <summary>
- /// Instantiates the scene's node hierarchy, returning null on failure.
+ /// Instantiates the scene's node hierarchy, returning <see langword="null"/> on failure.
/// Triggers child scene instantiation(s). Triggers a
- /// `Node.NotificationInstanced` notification on the root node.
+ /// <see cref="Node.NotificationInstanced"/> notification on the root node.
/// </summary>
- /// <typeparam name="T">The type to cast to. Should be a descendant of Node.</typeparam>
+ /// <seealso cref="Instantiate{T}(GenEditState)"/>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
+ /// <returns>The instantiated scene.</returns>
public T InstantiateOrNull<T>(PackedScene.GenEditState editState = (PackedScene.GenEditState)0) where T : class
{
return Instantiate(editState) as T;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
index 74fa05d1fd..25c11d5cf6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/ResourceLoaderExtensions.cs
@@ -1,10 +1,30 @@
+using System;
+
namespace Godot
{
public static partial class ResourceLoader
{
- public static T Load<T>(string path, string typeHint = null, CacheMode noCache = CacheMode.Reuse) where T : class
+ /// <summary>
+ /// Loads a resource at the given <paramref name="path"/>, caching the result
+ /// for further access.
+ /// The registered <see cref="ResourceFormatLoader"/> instances are queried sequentially
+ /// to find the first one which can handle the file's extension, and then attempt
+ /// loading. If loading fails, the remaining ResourceFormatLoaders are also attempted.
+ /// An optional <paramref name="typeHint"/> can be used to further specify the
+ /// <see cref="Resource"/> type that should be handled by the <see cref="ResourceFormatLoader"/>.
+ /// Anything that inherits from <see cref="Resource"/> can be used as a type hint,
+ /// for example <see cref="Image"/>.
+ /// The <paramref name="cacheMode"/> property defines whether and how the cache should
+ /// be used or updated when loading the resource. See <see cref="CacheMode"/> for details.
+ /// Returns an empty resource if no <see cref="ResourceFormatLoader"/> could handle the file.
+ /// </summary>
+ /// <exception cref="InvalidCastException">
+ /// Thrown when the given the loaded resource can't be casted to the given type <typeparamref name="T"/>.
+ /// </exception>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam>
+ public static T Load<T>(string path, string typeHint = null, CacheMode cacheMode = CacheMode.Reuse) where T : class
{
- return (T)(object)Load(path, typeHint, noCache);
+ return (T)(object)Load(path, typeHint, cacheMode);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
index 20b11a48dd..df130a5c77 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/SceneTreeExtensions.cs
@@ -6,12 +6,16 @@ namespace Godot
{
public partial class SceneTree
{
+ /// <summary>
+ /// Returns a list of all nodes assigned to the given <paramref name="group"/>.
+ /// </summary>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
public Array<T> GetNodesInGroup<T>(StringName group) where T : class
{
- return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(Object.GetPtr(this), StringName.GetPtr(group), typeof(T)));
+ return new Array<T>(godot_icall_SceneTree_get_nodes_in_group_Generic(GetPtr(this), StringName.GetPtr(group), typeof(T)));
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
+ internal static extern IntPtr godot_icall_SceneTree_get_nodes_in_group_Generic(IntPtr obj, IntPtr group, Type elemType);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
index 71d0593916..c664463e86 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -7,22 +7,56 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
-// TODO: Add comments describing what this class does. It is not obvious.
-
namespace Godot
{
+ /// <summary>
+ /// Godot's global functions.
+ /// </summary>
public static partial class GD
{
+ /// <summary>
+ /// Decodes a byte array back to a <c>Variant</c> value.
+ /// If <paramref name="allowObjects"/> is <see langword="true"/> decoding objects is allowed.
+ ///
+ /// WARNING: Deserialized object can contain code which gets executed.
+ /// Do not set <paramref name="allowObjects"/> to <see langword="true"/>
+ /// if the serialized object comes from untrusted sources to avoid
+ /// potential security threats (remote code execution).
+ /// </summary>
+ /// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
+ /// <param name="allowObjects">If objects should be decoded.</param>
+ /// <returns>The decoded <c>Variant</c>.</returns>
public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
{
return godot_icall_GD_bytes2var(bytes, allowObjects);
}
+ /// <summary>
+ /// Converts from a <c>Variant</c> type to another in the best way possible.
+ /// The <paramref name="type"/> parameter uses the <see cref="Variant.Type"/> values.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// var a = new Vector2(1, 0);
+ /// // Prints 1
+ /// GD.Print(a.Length());
+ /// var b = GD.Convert(a, Variant.Type.String)
+ /// // Prints 6 as "(1, 0)" is 6 characters
+ /// GD.Print(b.Length);
+ /// </code>
+ /// </example>
+ /// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
public static object Convert(object what, Variant.Type type)
{
return godot_icall_GD_convert(what, type);
}
+ /// <summary>
+ /// Converts from decibels to linear energy (audio).
+ /// </summary>
+ /// <seealso cref="Linear2Db(real_t)"/>
+ /// <param name="db">Decibels to convert.</param>
+ /// <returns>Audio volume as linear energy.</returns>
public static real_t Db2Linear(real_t db)
{
return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
@@ -38,111 +72,370 @@ namespace Godot
return Array.ConvertAll(parameters, x => x?.ToString() ?? "null");
}
+ /// <summary>
+ /// Returns the integer hash of the variable passed.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(GD.Hash("a")); // Prints 177670
+ /// </code>
+ /// </example>
+ /// <param name="var">Variable that will be hashed.</param>
+ /// <returns>Hash of the variable passed.</returns>
public static int Hash(object var)
{
return godot_icall_GD_hash(var);
}
+ /// <summary>
+ /// Returns the <see cref="Object"/> that corresponds to <paramref name="instanceId"/>.
+ /// All Objects have a unique instance ID.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// public class MyNode : Node
+ /// {
+ /// public string foo = "bar";
+ ///
+ /// public override void _Ready()
+ /// {
+ /// ulong id = GetInstanceId();
+ /// var inst = (MyNode)GD.InstanceFromId(Id);
+ /// GD.Print(inst.foo); // Prints bar
+ /// }
+ /// }
+ /// </code>
+ /// </example>
+ /// <param name="instanceId">Instance ID of the Object to retrieve.</param>
+ /// <returns>The <see cref="Object"/> instance.</returns>
public static Object InstanceFromId(ulong instanceId)
{
return godot_icall_GD_instance_from_id(instanceId);
}
+ /// <summary>
+ /// Converts from linear energy to decibels (audio).
+ /// This can be used to implement volume sliders that behave as expected (since volume isn't linear).
+ /// </summary>
+ /// <seealso cref="Db2Linear(real_t)"/>
+ /// <example>
+ /// <code>
+ /// // "slider" refers to a node that inherits Range such as HSlider or VSlider.
+ /// // Its range must be configured to go from 0 to 1.
+ /// // Change the bus name if you'd like to change the volume of a specific bus only.
+ /// AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), GD.Linear2Db(slider.value));
+ /// </code>
+ /// </example>
+ /// <param name="linear">The linear energy to convert.</param>
+ /// <returns>Audio as decibels.</returns>
public static real_t Linear2Db(real_t linear)
{
return (real_t)(Math.Log(linear) * 8.6858896380650365530225783783321);
}
+ /// <summary>
+ /// Loads a resource from the filesystem located at <paramref name="path"/>.
+ /// The resource is loaded on the method call (unless it's referenced already
+ /// elsewhere, e.g. in another script or in the scene), which might cause slight delay,
+ /// especially when loading scenes. To avoid unnecessary delays when loading something
+ /// multiple times, either store the resource in a variable.
+ ///
+ /// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem
+ /// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ ///
+ /// Important: The path must be absolute, a local path will just return <see langword="null"/>.
+ /// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used
+ /// for more advanced scenarios.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// // Load a scene called main located in the root of the project directory and cache it in a variable.
+ /// var main = GD.Load("res://main.tscn"); // main will contain a PackedScene resource.
+ /// </code>
+ /// </example>
+ /// <param name="path">Path of the <see cref="Resource"/> to load.</param>
+ /// <returns>The loaded <see cref="Resource"/>.</returns>
public static Resource Load(string path)
{
return ResourceLoader.Load(path);
}
+ /// <summary>
+ /// Loads a resource from the filesystem located at <paramref name="path"/>.
+ /// The resource is loaded on the method call (unless it's referenced already
+ /// elsewhere, e.g. in another script or in the scene), which might cause slight delay,
+ /// especially when loading scenes. To avoid unnecessary delays when loading something
+ /// multiple times, either store the resource in a variable.
+ ///
+ /// Note: Resource paths can be obtained by right-clicking on a resource in the FileSystem
+ /// dock and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
+ ///
+ /// Important: The path must be absolute, a local path will just return <see langword="null"/>.
+ /// This method is a simplified version of <see cref="ResourceLoader.Load"/>, which can be used
+ /// for more advanced scenarios.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// // Load a scene called main located in the root of the project directory and cache it in a variable.
+ /// var main = GD.Load&lt;PackedScene&gt;("res://main.tscn"); // main will contain a PackedScene resource.
+ /// </code>
+ /// </example>
+ /// <param name="path">Path of the <see cref="Resource"/> to load.</param>
+ /// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Resource"/>.</typeparam>
public static T Load<T>(string path) where T : class
{
return ResourceLoader.Load<T>(path);
}
+ /// <summary>
+ /// Pushes an error message to Godot's built-in debugger and to the OS terminal.
+ ///
+ /// Note: Errors printed this way will not pause project execution.
+ /// To print an error message and pause project execution in debug builds,
+ /// use [code]assert(false, "test error")[/code] instead.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PushError("test_error"); // Prints "test error" to debugger and terminal as error call
+ /// </code>
+ /// </example>
+ /// <param name="message">Error message.</param>
public static void PushError(string message)
{
godot_icall_GD_pusherror(message);
}
+ /// <summary>
+ /// Pushes a warning message to Godot's built-in debugger and to the OS terminal.
+ /// </summary>
+ /// <example>
+ /// GD.PushWarning("test warning"); // Prints "test warning" to debugger and terminal as warning call
+ /// </example>
+ /// <param name="message">Warning message.</param>
public static void PushWarning(string message)
{
godot_icall_GD_pushwarning(message);
}
+ /// <summary>
+ /// Converts one or more arguments of any type to string in the best way possible
+ /// and prints them to the console.
+ ///
+ /// Note: Consider using <see cref="PushError(string)"/> and <see cref="PushWarning(string)"/>
+ /// to print error and warning messages instead of <see cref="Print(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>
+ /// var a = new int[] { 1, 2, 3 };
+ /// GD.Print("a", "b", a); // Prints ab[1, 2, 3]
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
public static void Print(params object[] what)
{
godot_icall_GD_print(GetPrintParams(what));
}
+ /// <summary>
+ /// Prints the current stack trace information to the console.
+ /// </summary>
public static void PrintStack()
{
Print(System.Environment.StackTrace);
}
+ /// <summary>
+ /// Prints one or more arguments to strings in the best way possible to standard error line.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintErr("prints to stderr");
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
public static void PrintErr(params object[] what)
{
godot_icall_GD_printerr(GetPrintParams(what));
}
+ /// <summary>
+ /// Prints one or more arguments to strings in the best way possible to console.
+ /// No newline is added at the end.
+ ///
+ /// Note: Due to limitations with Godot's built-in console, this only prints to the terminal.
+ /// If you need to print in the editor, use another method, such as <see cref="Print(object[])"/>.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintRaw("A");
+ /// GD.PrintRaw("B");
+ /// // Prints AB
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
public static void PrintRaw(params object[] what)
{
godot_icall_GD_printraw(GetPrintParams(what));
}
+ /// <summary>
+ /// Prints one or more arguments to the console with a space between each argument.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintS("A", "B", "C"); // Prints A B C
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
public static void PrintS(params object[] what)
{
godot_icall_GD_prints(GetPrintParams(what));
}
+ /// <summary>
+ /// Prints one or more arguments to the console with a tab between each argument.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintT("A", "B", "C"); // Prints A B C
+ /// </code>
+ /// </example>
+ /// <param name="what">Arguments that will be printed.</param>
public static void PrintT(params object[] what)
{
godot_icall_GD_printt(GetPrintParams(what));
}
+ /// <summary>
+ /// Returns a random floating point value between <c>0.0</c> and <c>1.0</c> (inclusive).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Randf(); // Returns e.g. 0.375671
+ /// </code>
+ /// </example>
+ /// <returns>A random <see langword="float"/> number.</returns>
public static float Randf()
{
return godot_icall_GD_randf();
}
+ /// <summary>
+ /// Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified <c>mean</c> and a standard <c>deviation</c>.
+ /// This is also called Gaussian distribution.
+ /// </summary>
+ /// <returns>A random normally-distributed <see langword="float"/> number.</returns>
+ public static double Randfn(double mean, double deviation)
+ {
+ return godot_icall_GD_randfn(mean, deviation);
+ }
+
+ /// <summary>
+ /// Returns a random unsigned 32-bit integer.
+ /// Use remainder to obtain a random value in the interval <c>[0, N - 1]</c> (where N is smaller than 2^32).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Randi(); // Returns random integer between 0 and 2^32 - 1
+ /// GD.Randi() % 20; // Returns random integer between 0 and 19
+ /// GD.Randi() % 100; // Returns random integer between 0 and 99
+ /// GD.Randi() % 100 + 1; // Returns random integer between 1 and 100
+ /// </code>
+ /// </example>
+ /// <returns>A random <see langword="uint"/> number.</returns>
public static uint Randi()
{
return godot_icall_GD_randi();
}
+ /// <summary>
+ /// Randomizes the seed (or the internal state) of the random number generator.
+ /// Current implementation reseeds using a number based on time.
+ ///
+ /// Note: This method is called automatically when the project is run.
+ /// If you need to fix the seed to have reproducible results, use <see cref="Seed(ulong)"/>
+ /// to initialize the random number generator.
+ /// </summary>
public static void Randomize()
{
godot_icall_GD_randomize();
}
+ /// <summary>
+ /// Returns a random floating point value on the interval between <paramref name="from"/>
+ /// and <paramref name="to"/> (inclusive).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.PrintS(GD.RandRange(-10.0, 10.0), GD.RandRange(-10.0, 10.0)); // Prints e.g. -3.844535 7.45315
+ /// </code>
+ /// </example>
+ /// <returns>A random <see langword="double"/> number inside the given range.</returns>
public static double RandRange(double from, double to)
{
return godot_icall_GD_randf_range(from, to);
}
+ /// <summary>
+ /// Returns a random signed 32-bit integer between <paramref name="from"/>
+ /// and <paramref name="to"/> (inclusive). If <paramref name="to"/> is lesser than
+ /// <paramref name="from"/>, they are swapped.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(GD.RandRange(0, 1)); // Prints 0 or 1
+ /// GD.Print(GD.RandRange(-10, 1000)); // Prints any number from -10 to 1000
+ /// </code>
+ /// </example>
+ /// <returns>A random <see langword="int"/> number inside the given range.</returns>
public static int RandRange(int from, int to)
{
return godot_icall_GD_randi_range(from, to);
}
+ /// <summary>
+ /// Returns a random unsigned 32-bit integer, using the given <paramref name="seed"/>.
+ /// </summary>
+ /// <param name="seed">
+ /// Seed to use to generate the random number.
+ /// If a different seed is used, its value will be modfied.
+ /// </param>
+ /// <returns>A random <see langword="uint"/> number.</returns>
public static uint RandFromSeed(ref ulong seed)
{
return godot_icall_GD_rand_seed(seed, out seed);
}
+ /// <summary>
+ /// Returns a <see cref="IEnumerable{T}"/> that iterates from
+ /// <c>0</c> to <paramref name="end"/> in steps of <c>1</c>.
+ /// </summary>
+ /// <param name="end">The last index.</param>
public static IEnumerable<int> Range(int end)
{
return Range(0, end, 1);
}
+ /// <summary>
+ /// Returns a <see cref="IEnumerable{T}"/> that iterates from
+ /// <paramref name="start"/> to <paramref name="end"/> in steps of <c>1</c>.
+ /// </summary>
+ /// <param name="start">The first index.</param>
+ /// <param name="end">The last index.</param>
public static IEnumerable<int> Range(int start, int end)
{
return Range(start, end, 1);
}
+ /// <summary>
+ /// Returns a <see cref="IEnumerable{T}"/> that iterates from
+ /// <paramref name="start"/> to <paramref name="end"/> in steps of <paramref name="step"/>.
+ /// </summary>
+ /// <param name="start">The first index.</param>
+ /// <param name="end">The last index.</param>
+ /// <param name="step">The amount by which to increment the index on each iteration.</param>
public static IEnumerable<int> Range(int start, int end, int step)
{
if (end < start && step > 0)
@@ -163,109 +456,167 @@ namespace Godot
}
}
+ /// <summary>
+ /// Sets seed for the random number generator.
+ /// </summary>
+ /// <param name="seed">Seed that will be used.</param>
public static void Seed(ulong seed)
{
godot_icall_GD_seed(seed);
}
+ /// <summary>
+ /// Converts one or more arguments of any type to string in the best way possible.
+ /// </summary>
+ /// <param name="what">Arguments that will converted to string.</param>
+ /// <returns>The string formed by the given arguments.</returns>
public static string Str(params object[] what)
{
return godot_icall_GD_str(what);
}
+ /// <summary>
+ /// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// string a = "{\"a\": 1, \"b\": 2 }";
+ /// var b = (Godot.Collections.Dictionary)GD.Str2Var(a);
+ /// GD.Print(b["a"]); // Prints 1
+ /// </code>
+ /// </example>
+ /// <param name="str">String that will be converted to Variant.</param>
+ /// <returns>The decoded <c>Variant</c>.</returns>
public static object Str2Var(string str)
{
return godot_icall_GD_str2var(str);
}
+ /// <summary>
+ /// Returns whether the given class exists in <see cref="ClassDB"/>.
+ /// </summary>
+ /// <returns>If the class exists in <see cref="ClassDB"/>.</returns>
public static bool TypeExists(StringName type)
{
return godot_icall_GD_type_exists(StringName.GetPtr(type));
}
+ /// <summary>
+ /// Encodes a <c>Variant</c> value to a byte array.
+ /// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
+ /// (and can potentially include code).
+ /// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>.
+ /// </summary>
+ /// <param name="var">Variant that will be encoded.</param>
+ /// <param name="fullObjects">If objects should be serialized.</param>
+ /// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
public static byte[] Var2Bytes(object var, bool fullObjects = false)
{
return godot_icall_GD_var2bytes(var, fullObjects);
}
+ /// <summary>
+ /// Converts a <c>Variant</c> <paramref name="var"/> to a formatted string that
+ /// can later be parsed using <see cref="Str2Var(string)"/>.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// var a = new Godot.Collections.Dictionary { ["a"] = 1, ["b"] = 2 };
+ /// GD.Print(GD.Var2Str(a));
+ /// // Prints
+ /// // {
+ /// // "a": 1,
+ /// // "b": 2
+ /// // }
+ /// </code>
+ /// </example>
+ /// <param name="var">Variant that will be converted to string.</param>
+ /// <returns>The <c>Variant</c> encoded as a string.</returns>
public static string Var2Str(object var)
{
return godot_icall_GD_var2str(var);
}
+ /// <summary>
+ /// Get the <see cref="Variant.Type"/> that corresponds for the given <see cref="Type"/>.
+ /// </summary>
+ /// <returns>The <see cref="Variant.Type"/> for the given <paramref name="type"/>.</returns>
public static Variant.Type TypeToVariantType(Type type)
{
return godot_icall_TypeToVariantType(type);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
+ internal static extern object godot_icall_GD_bytes2var(byte[] bytes, bool allowObjects);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern object godot_icall_GD_convert(object what, Variant.Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_convert(object what, Variant.Type type);
+ internal static extern int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_GD_hash(object var);
+ internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
+ internal static extern void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_print(object[] what);
+ internal static extern void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printerr(object[] what);
+ internal static extern void godot_icall_GD_printraw(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printraw(object[] what);
+ internal static extern void godot_icall_GD_prints(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_prints(object[] what);
+ internal static extern void godot_icall_GD_printt(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printt(object[] what);
+ internal static extern void godot_icall_GD_randomize();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static float godot_icall_GD_randf();
+ internal static extern uint godot_icall_GD_randi();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static uint godot_icall_GD_randi();
+ internal static extern float godot_icall_GD_randf();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_randomize();
+ internal static extern int godot_icall_GD_randi_range(int from, int to);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static double godot_icall_GD_randf_range(double from, double to);
+ internal static extern double godot_icall_GD_randf_range(double from, double to);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_GD_randi_range(int from, int to);
+ internal static extern double godot_icall_GD_randfn(double mean, double deviation);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
+ internal static extern uint godot_icall_GD_rand_seed(ulong seed, out ulong newSeed);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_seed(ulong seed);
+ internal static extern void godot_icall_GD_seed(ulong seed);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_GD_str(object[] what);
+ internal static extern string godot_icall_GD_str(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_GD_str2var(string str);
+ internal static extern object godot_icall_GD_str2var(string str);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_GD_type_exists(IntPtr type);
+ internal static extern bool godot_icall_GD_type_exists(IntPtr type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
+ internal static extern byte[] godot_icall_GD_var2bytes(object what, bool fullObjects);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_GD_var2str(object var);
+ internal static extern string godot_icall_GD_var2str(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_pusherror(string type);
+ internal static extern void godot_icall_GD_pusherror(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_pushwarning(string type);
+ internal static extern void godot_icall_GD_pushwarning(string type);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern Variant.Type godot_icall_TypeToVariantType(Type type);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
index 4b5e3f8761..c01c926e82 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs
@@ -6,7 +6,8 @@ namespace Godot
{
public class GodotSynchronizationContext : SynchronizationContext
{
- private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue = new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
+ private readonly BlockingCollection<KeyValuePair<SendOrPostCallback, object>> _queue =
+ new BlockingCollection<KeyValuePair<SendOrPostCallback, object>>();
public override void Post(SendOrPostCallback d, object state)
{
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
index a566b53659..9ccac1faaf 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
@@ -24,7 +24,7 @@ namespace Godot
try
{
- var stackTrace = new StackTrace(true).ToString();
+ string stackTrace = new StackTrace(true).ToString();
GD.PrintErr(stackTrace);
}
catch
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
index f508211f68..ee4d0eed08 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
@@ -3,60 +3,147 @@ using System.Collections.Generic;
namespace Godot
{
- static class MarshalUtils
+ internal static class MarshalUtils
{
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Godot.Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
+ /// is <see cref="Collections.Array{T}"/>; otherwise returns <see langword="false"/>.
/// </summary>
- /// <exception cref="System.InvalidOperationException">
- /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
/// </exception>
- static bool TypeIsGenericArray(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Godot.Collections.Array<>);
+ private static bool TypeIsGenericArray(Type type) =>
+ type.GetGenericTypeDefinition() == typeof(Collections.Array<>);
/// <summary>
/// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
- /// is <see cref="Godot.Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
+ /// is <see cref="Collections.Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
/// </summary>
- /// <exception cref="System.InvalidOperationException">
- /// <paramref name="type"/> is not a generic type. That is, IsGenericType returns false.
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
/// </exception>
- static bool TypeIsGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(Godot.Collections.Dictionary<,>);
+ private static bool TypeIsGenericDictionary(Type type) =>
+ type.GetGenericTypeDefinition() == typeof(Collections.Dictionary<,>);
- static bool TypeIsSystemGenericList(Type type) =>
- type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.List<>);
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="List{T}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static bool TypeIsSystemGenericList(Type type) =>
+ type.GetGenericTypeDefinition() == typeof(List<>);
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="Dictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static bool TypeIsSystemGenericDictionary(Type type) =>
+ type.GetGenericTypeDefinition() == typeof(Dictionary<,>);
- static bool TypeIsSystemGenericDictionary(Type type) =>
- type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>);
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="IEnumerable{T}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
- static bool TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="ICollection{T}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
- static bool TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
+ /// <summary>
+ /// Returns <see langword="true"/> if the generic type definition of <paramref name="type"/>
+ /// is <see cref="IDictionary{TKey, TValue}"/>; otherwise returns <see langword="false"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
- static bool TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
+ /// <summary>
+ /// Returns the generic type definition of <paramref name="type"/>.
+ /// </summary>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="type"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static void GetGenericTypeDefinition(Type type, out Type genericTypeDefinition)
+ {
+ genericTypeDefinition = type.GetGenericTypeDefinition();
+ }
- static void ArrayGetElementType(Type arrayType, out Type elementType)
+ /// <summary>
+ /// Gets the element type for the given <paramref name="arrayType"/>.
+ /// </summary>
+ /// <param name="arrayType">Type for the generic array.</param>
+ /// <param name="elementType">Element type for the generic array.</param>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="arrayType"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static void ArrayGetElementType(Type arrayType, out Type elementType)
{
elementType = arrayType.GetGenericArguments()[0];
}
- static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
+ /// <summary>
+ /// Gets the key type and the value type for the given <paramref name="dictionaryType"/>.
+ /// </summary>
+ /// <param name="dictionaryType">The type for the generic dictionary.</param>
+ /// <param name="keyType">Key type for the generic dictionary.</param>
+ /// <param name="valueType">Value type for the generic dictionary.</param>
+ /// <exception cref="InvalidOperationException">
+ /// Thrown when the given <paramref name="dictionaryType"/> is not a generic type.
+ /// That is, <see cref="Type.IsGenericType"/> returns <see langword="false"/>.
+ /// </exception>
+ private static void DictionaryGetKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType)
{
var genericArgs = dictionaryType.GetGenericArguments();
keyType = genericArgs[0];
valueType = genericArgs[1];
}
- static Type MakeGenericArrayType(Type elemType)
+ /// <summary>
+ /// Constructs a new <see cref="Type"/> from <see cref="Collections.Array{T}"/>
+ /// where the generic type for the elements is <paramref name="elemType"/>.
+ /// </summary>
+ /// <param name="elemType">Element type for the array.</param>
+ /// <returns>The generic array type with the specified element type.</returns>
+ private static Type MakeGenericArrayType(Type elemType)
{
- return typeof(Godot.Collections.Array<>).MakeGenericType(elemType);
+ return typeof(Collections.Array<>).MakeGenericType(elemType);
}
- static Type MakeGenericDictionaryType(Type keyType, Type valueType)
+ /// <summary>
+ /// Constructs a new <see cref="Type"/> from <see cref="Collections.Dictionary{TKey, TValue}"/>
+ /// where the generic type for the keys is <paramref name="keyType"/> and
+ /// for the values is <paramref name="valueType"/>.
+ /// </summary>
+ /// <param name="keyType">Key type for the dictionary.</param>
+ /// <param name="valueType">Key type for the dictionary.</param>
+ /// <returns>The generic dictionary type with the specified key and value types.</returns>
+ private static Type MakeGenericDictionaryType(Type keyType, Type valueType)
{
- return typeof(Godot.Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
+ return typeof(Collections.Dictionary<,>).MakeGenericType(keyType, valueType);
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
index 213f84ad73..bfe9600084 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -7,6 +7,9 @@ using System;
namespace Godot
{
+ /// <summary>
+ /// Provides constants and static methods for common mathematical functions.
+ /// </summary>
public static partial class Mathf
{
// Define constants with Decimal precision and cast down to double or float.
@@ -19,121 +22,120 @@ namespace Godot
/// <summary>
/// Constant that represents how many times the diameter of a circle
- /// fits around its perimeter. This is equivalent to `Mathf.Tau / 2`.
+ /// fits around its perimeter. This is equivalent to <c>Mathf.Tau / 2</c>.
/// </summary>
// 3.1415927f and 3.14159265358979
public const real_t Pi = (real_t)3.1415926535897932384626433833M;
/// <summary>
- /// Positive infinity. For negative infinity, use `-Mathf.Inf`.
+ /// Positive infinity. For negative infinity, use <c>-Mathf.Inf</c>.
/// </summary>
public const real_t Inf = real_t.PositiveInfinity;
/// <summary>
- /// "Not a Number", an invalid value. `NaN` has special properties, including
+ /// "Not a Number", an invalid value. <c>NaN</c> has special properties, including
/// that it is not equal to itself. It is output by some invalid operations,
/// such as dividing zero by zero.
/// </summary>
public const real_t NaN = real_t.NaN;
// 0.0174532924f and 0.0174532925199433
- private const real_t Deg2RadConst = (real_t)0.0174532925199432957692369077M;
+ private const real_t _deg2RadConst = (real_t)0.0174532925199432957692369077M;
// 57.29578f and 57.2957795130823
- private const real_t Rad2DegConst = (real_t)57.295779513082320876798154814M;
+ private const real_t _rad2DegConst = (real_t)57.295779513082320876798154814M;
/// <summary>
- /// Returns the absolute value of `s` (i.e. positive value).
+ /// Returns the absolute value of <paramref name="s"/> (i.e. positive value).
/// </summary>
/// <param name="s">The input number.</param>
- /// <returns>The absolute value of `s`.</returns>
+ /// <returns>The absolute value of <paramref name="s"/>.</returns>
public static int Abs(int s)
{
return Math.Abs(s);
}
/// <summary>
- /// Returns the absolute value of `s` (i.e. positive value).
+ /// Returns the absolute value of <paramref name="s"/> (i.e. positive value).
/// </summary>
/// <param name="s">The input number.</param>
- /// <returns>The absolute value of `s`.</returns>
+ /// <returns>The absolute value of <paramref name="s"/>.</returns>
public static real_t Abs(real_t s)
{
return Math.Abs(s);
}
/// <summary>
- /// Returns the arc cosine of `s` in radians. Use to get the angle of cosine s.
+ /// Returns the arc cosine of <paramref name="s"/> in radians.
+ /// Use to get the angle of cosine <paramref name="s"/>.
/// </summary>
/// <param name="s">The input cosine value. Must be on the range of -1.0 to 1.0.</param>
- /// <returns>An angle that would result in the given cosine value. On the range `0` to `Tau/2`.</returns>
+ /// <returns>
+ /// An angle that would result in the given cosine value. On the range <c>0</c> to <c>Tau/2</c>.
+ /// </returns>
public static real_t Acos(real_t s)
{
return (real_t)Math.Acos(s);
}
/// <summary>
- /// Returns the arc sine of `s` in radians. Use to get the angle of sine s.
+ /// Returns the arc sine of <paramref name="s"/> in radians.
+ /// Use to get the angle of sine <paramref name="s"/>.
/// </summary>
/// <param name="s">The input sine value. Must be on the range of -1.0 to 1.0.</param>
- /// <returns>An angle that would result in the given sine value. On the range `-Tau/4` to `Tau/4`.</returns>
+ /// <returns>
+ /// An angle that would result in the given sine value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
+ /// </returns>
public static real_t Asin(real_t s)
{
return (real_t)Math.Asin(s);
}
/// <summary>
- /// Returns the arc tangent of `s` in radians. Use to get the angle of tangent s.
+ /// Returns the arc tangent of <paramref name="s"/> in radians.
+ /// Use to get the angle of tangent <paramref name="s"/>.
///
/// The method cannot know in which quadrant the angle should fall.
- /// See <see cref="Atan2(real_t, real_t)"/> if you have both `y` and `x`.
+ /// See <see cref="Atan2(real_t, real_t)"/> if you have both <c>y</c> and <c>x</c>.
/// </summary>
/// <param name="s">The input tangent value.</param>
- /// <returns>An angle that would result in the given tangent value. On the range `-Tau/4` to `Tau/4`.</returns>
+ /// <returns>
+ /// An angle that would result in the given tangent value. On the range <c>-Tau/4</c> to <c>Tau/4</c>.
+ /// </returns>
public static real_t Atan(real_t s)
{
return (real_t)Math.Atan(s);
}
/// <summary>
- /// Returns the arc tangent of `y` and `x` in radians. Use to get the angle
- /// of the tangent of `y/x`. To compute the value, the method takes into
+ /// Returns the arc tangent of <paramref name="y"/> and <paramref name="x"/> in radians.
+ /// Use to get the angle of the tangent of <c>y/x</c>. To compute the value, the method takes into
/// account the sign of both arguments in order to determine the quadrant.
///
/// Important note: The Y coordinate comes first, by convention.
/// </summary>
/// <param name="y">The Y coordinate of the point to find the angle to.</param>
/// <param name="x">The X coordinate of the point to find the angle to.</param>
- /// <returns>An angle that would result in the given tangent value. On the range `-Tau/2` to `Tau/2`.</returns>
+ /// <returns>
+ /// An angle that would result in the given tangent value. On the range <c>-Tau/2</c> to <c>Tau/2</c>.
+ /// </returns>
public static real_t Atan2(real_t y, real_t x)
{
return (real_t)Math.Atan2(y, x);
}
/// <summary>
- /// Converts a 2D point expressed in the cartesian coordinate
- /// system (X and Y axis) to the polar coordinate system
- /// (a distance from the origin and an angle).
- /// </summary>
- /// <param name="x">The input X coordinate.</param>
- /// <param name="y">The input Y coordinate.</param>
- /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns>
- public static Vector2 Cartesian2Polar(real_t x, real_t y)
- {
- return new Vector2(Sqrt(x * x + y * y), Atan2(y, x));
- }
-
- /// <summary>
- /// Rounds `s` upward (towards positive infinity).
+ /// Rounds <paramref name="s"/> upward (towards positive infinity).
/// </summary>
/// <param name="s">The number to ceil.</param>
- /// <returns>The smallest whole number that is not less than `s`.</returns>
+ /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
public static real_t Ceil(real_t s)
{
return (real_t)Math.Ceiling(s);
}
/// <summary>
- /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// Clamps a <paramref name="value"/> so that it is not less than <paramref name="min"/>
+ /// and not more than <paramref name="max"/>.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
@@ -145,7 +147,8 @@ namespace Godot
}
/// <summary>
- /// Clamps a `value` so that it is not less than `min` and not more than `max`.
+ /// Clamps a <paramref name="value"/> so that it is not less than <paramref name="min"/>
+ /// and not more than <paramref name="max"/>.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="min">The minimum allowed value.</param>
@@ -157,7 +160,7 @@ namespace Godot
}
/// <summary>
- /// Returns the cosine of angle `s` in radians.
+ /// Returns the cosine of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The cosine of that angle.</returns>
@@ -167,7 +170,7 @@ namespace Godot
}
/// <summary>
- /// Returns the hyperbolic cosine of angle `s` in radians.
+ /// Returns the hyperbolic cosine of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic cosine of that angle.</returns>
@@ -183,16 +186,18 @@ namespace Godot
/// <returns>The same angle expressed in radians.</returns>
public static real_t Deg2Rad(real_t deg)
{
- return deg * Deg2RadConst;
+ return deg * _deg2RadConst;
}
/// <summary>
- /// Easing function, based on exponent. The curve values are:
- /// `0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.
+ /// Easing function, based on exponent. The <paramref name="curve"/> values are:
+ /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out.
/// Negative values are in-out/out-in.
/// </summary>
/// <param name="s">The value to ease.</param>
- /// <param name="curve">`0` is constant, `1` is linear, `0` to `1` is ease-in, `1` or more is ease-out.</param>
+ /// <param name="curve">
+ /// <c>0</c> is constant, <c>1</c> is linear, <c>0</c> to <c>1</c> is ease-in, <c>1</c> or more is ease-out.
+ /// </param>
/// <returns>The eased value.</returns>
public static real_t Ease(real_t s, real_t curve)
{
@@ -222,7 +227,7 @@ namespace Godot
return Pow(s * 2.0f, -curve) * 0.5f;
}
- return (1.0f - Pow(1.0f - (s - 0.5f) * 2.0f, -curve)) * 0.5f + 0.5f;
+ return ((1.0f - Pow(1.0f - ((s - 0.5f) * 2.0f), -curve)) * 0.5f) + 0.5f;
}
return 0f;
@@ -230,20 +235,20 @@ namespace Godot
/// <summary>
/// The natural exponential function. It raises the mathematical
- /// constant `e` to the power of `s` and returns it.
+ /// constant <c>e</c> to the power of <paramref name="s"/> and returns it.
/// </summary>
- /// <param name="s">The exponent to raise `e` to.</param>
- /// <returns>`e` raised to the power of `s`.</returns>
+ /// <param name="s">The exponent to raise <c>e</c> to.</param>
+ /// <returns><c>e</c> raised to the power of <paramref name="s"/>.</returns>
public static real_t Exp(real_t s)
{
return (real_t)Math.Exp(s);
}
/// <summary>
- /// Rounds `s` downward (towards negative infinity).
+ /// Rounds <paramref name="s"/> downward (towards negative infinity).
/// </summary>
/// <param name="s">The number to floor.</param>
- /// <returns>The largest whole number that is not more than `s`.</returns>
+ /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
public static real_t Floor(real_t s)
{
return (real_t)Math.Floor(s);
@@ -263,12 +268,13 @@ namespace Godot
}
/// <summary>
- /// Returns true if `a` and `b` are approximately equal to each other.
+ /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately equal
+ /// to each other.
/// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
/// </summary>
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
- /// <returns>A bool for whether or not the two values are approximately equal.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the two values are approximately equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b)
{
// Check for exact equality first, required to handle "infinity" values.
@@ -286,33 +292,33 @@ namespace Godot
}
/// <summary>
- /// Returns whether `s` is an infinity value (either positive infinity or negative infinity).
+ /// Returns whether <paramref name="s"/> is an infinity value (either positive infinity or negative infinity).
/// </summary>
/// <param name="s">The value to check.</param>
- /// <returns>A bool for whether or not the value is an infinity value.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the value is an infinity value.</returns>
public static bool IsInf(real_t s)
{
return real_t.IsInfinity(s);
}
/// <summary>
- /// Returns whether `s` is a `NaN` ("Not a Number" or invalid) value.
+ /// Returns whether <paramref name="s"/> is a <c>NaN</c> ("Not a Number" or invalid) value.
/// </summary>
/// <param name="s">The value to check.</param>
- /// <returns>A bool for whether or not the value is a `NaN` value.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the value is a <c>NaN</c> value.</returns>
public static bool IsNaN(real_t s)
{
return real_t.IsNaN(s);
}
/// <summary>
- /// Returns true if `s` is approximately zero.
+ /// Returns <see langword="true"/> if <paramref name="s"/> is approximately zero.
/// The comparison is done using a tolerance calculation with <see cref="Epsilon"/>.
///
/// This method is faster than using <see cref="IsEqualApprox(real_t, real_t)"/> with one value as zero.
/// </summary>
/// <param name="s">The value to check.</param>
- /// <returns>A bool for whether or not the value is nearly zero.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the value is nearly zero.</returns>
public static bool IsZeroApprox(real_t s)
{
return Abs(s) < Epsilon;
@@ -328,7 +334,7 @@ namespace Godot
/// <returns>The resulting value of the interpolation.</returns>
public static real_t Lerp(real_t from, real_t to, real_t weight)
{
- return from + (to - from) * weight;
+ return from + ((to - from) * weight);
}
/// <summary>
@@ -345,7 +351,7 @@ namespace Godot
{
real_t difference = (to - from) % Mathf.Tau;
real_t distance = ((2 * difference) % Mathf.Tau) - difference;
- return from + distance * weight;
+ return from + (distance * weight);
}
/// <summary>
@@ -354,7 +360,7 @@ namespace Godot
/// Note: This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
/// </summary>
/// <param name="s">The input value.</param>
- /// <returns>The natural log of `s`.</returns>
+ /// <returns>The natural log of <paramref name="s"/>.</returns>
public static real_t Log(real_t s)
{
return (real_t)Math.Log(s);
@@ -405,9 +411,9 @@ namespace Godot
}
/// <summary>
- /// Moves `from` toward `to` by the `delta` value.
+ /// Moves <paramref name="from"/> toward <paramref name="to"/> by the <paramref name="delta"/> value.
///
- /// Use a negative delta value to move away.
+ /// Use a negative <paramref name="delta"/> value to move away.
/// </summary>
/// <param name="from">The start value.</param>
/// <param name="to">The value to move towards.</param>
@@ -415,11 +421,14 @@ namespace Godot
/// <returns>The value after moving.</returns>
public static real_t MoveToward(real_t from, real_t to, real_t delta)
{
- return Abs(to - from) <= delta ? to : from + Sign(to - from) * delta;
+ if (Abs(to - from) <= delta)
+ return to;
+
+ return from + (Sign(to - from) * delta);
}
/// <summary>
- /// Returns the nearest larger power of 2 for the integer `value`.
+ /// Returns the nearest larger power of 2 for the integer <paramref name="value"/>.
/// </summary>
/// <param name="value">The input value.</param>
/// <returns>The nearest larger power of 2.</returns>
@@ -436,23 +445,10 @@ namespace Godot
}
/// <summary>
- /// Converts a 2D point expressed in the polar coordinate
- /// system (a distance from the origin `r` and an angle `th`)
- /// to the cartesian coordinate system (X and Y axis).
- /// </summary>
- /// <param name="r">The distance from the origin.</param>
- /// <param name="th">The angle of the point.</param>
- /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns>
- public static Vector2 Polar2Cartesian(real_t r, real_t th)
- {
- return new Vector2(r * Cos(th), r * Sin(th));
- }
-
- /// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
+ /// Performs a canonical Modulus operation, where the output is on the range [0, <paramref name="b"/>).
/// </summary>
/// <param name="a">The dividend, the primary input.</param>
- /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param>
/// <returns>The resulting output.</returns>
public static int PosMod(int a, int b)
{
@@ -465,10 +461,10 @@ namespace Godot
}
/// <summary>
- /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`.
+ /// Performs a canonical Modulus operation, where the output is on the range [0, <paramref name="b"/>).
/// </summary>
/// <param name="a">The dividend, the primary input.</param>
- /// <param name="b">The divisor. The output is on the range `[0, b)`.</param>
+ /// <param name="b">The divisor. The output is on the range [0, <paramref name="b"/>).</param>
/// <returns>The resulting output.</returns>
public static real_t PosMod(real_t a, real_t b)
{
@@ -481,11 +477,11 @@ namespace Godot
}
/// <summary>
- /// Returns the result of `x` raised to the power of `y`.
+ /// Returns the result of <paramref name="x"/> raised to the power of <paramref name="y"/>.
/// </summary>
/// <param name="x">The base.</param>
/// <param name="y">The exponent.</param>
- /// <returns>`x` raised to the power of `y`.</returns>
+ /// <returns><paramref name="x"/> raised to the power of <paramref name="y"/>.</returns>
public static real_t Pow(real_t x, real_t y)
{
return (real_t)Math.Pow(x, y);
@@ -498,11 +494,11 @@ namespace Godot
/// <returns>The same angle expressed in degrees.</returns>
public static real_t Rad2Deg(real_t rad)
{
- return rad * Rad2DegConst;
+ return rad * _rad2DegConst;
}
/// <summary>
- /// Rounds `s` to the nearest whole number,
+ /// Rounds <paramref name="s"/> to the nearest whole number,
/// with halfway cases rounded towards the nearest multiple of two.
/// </summary>
/// <param name="s">The number to round.</param>
@@ -513,10 +509,11 @@ namespace Godot
}
/// <summary>
- /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>.
+ /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>.
/// </summary>
/// <param name="s">The input number.</param>
- /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
+ /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public static int Sign(int s)
{
if (s == 0)
@@ -525,10 +522,11 @@ namespace Godot
}
/// <summary>
- /// Returns the sign of `s`: `-1` or `1`. Returns `0` if `s` is `0`.
+ /// Returns the sign of <paramref name="s"/>: <c>-1</c> or <c>1</c>.
+ /// Returns <c>0</c> if <paramref name="s"/> is <c>0</c>.
/// </summary>
/// <param name="s">The input number.</param>
- /// <returns>One of three possible values: `1`, `-1`, or `0`.</returns>
+ /// <returns>One of three possible values: <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public static int Sign(real_t s)
{
if (s == 0)
@@ -537,7 +535,7 @@ namespace Godot
}
/// <summary>
- /// Returns the sine of angle `s` in radians.
+ /// Returns the sine of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The sine of that angle.</returns>
@@ -547,7 +545,7 @@ namespace Godot
}
/// <summary>
- /// Returns the hyperbolic sine of angle `s` in radians.
+ /// Returns the hyperbolic sine of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic sine of that angle.</returns>
@@ -557,8 +555,8 @@ namespace Godot
}
/// <summary>
- /// Returns a number smoothly interpolated between `from` and `to`,
- /// based on the `weight`. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
+ /// Returns a number smoothly interpolated between <paramref name="from"/> and <paramref name="to"/>,
+ /// based on the <paramref name="weight"/>. Similar to <see cref="Lerp(real_t, real_t, real_t)"/>,
/// but interpolates faster at the beginning and slower at the end.
/// </summary>
/// <param name="from">The start value for interpolation.</param>
@@ -572,16 +570,16 @@ namespace Godot
return from;
}
real_t x = Clamp((weight - from) / (to - from), (real_t)0.0, (real_t)1.0);
- return x * x * (3 - 2 * x);
+ return x * x * (3 - (2 * x));
}
/// <summary>
- /// Returns the square root of `s`, where `s` is a non-negative number.
+ /// Returns the square root of <paramref name="s"/>, where <paramref name="s"/> is a non-negative number.
///
- /// If you need negative inputs, use `System.Numerics.Complex`.
+ /// If you need negative inputs, use <see cref="System.Numerics.Complex"/>.
/// </summary>
/// <param name="s">The input number. Must not be negative.</param>
- /// <returns>The square root of `s`.</returns>
+ /// <returns>The square root of <paramref name="s"/>.</returns>
public static real_t Sqrt(real_t s)
{
return (real_t)Math.Sqrt(s);
@@ -596,7 +594,8 @@ namespace Godot
/// <returns>The position of the first non-zero digit.</returns>
public static int StepDecimals(real_t step)
{
- double[] sd = new double[] {
+ double[] sd = new double[]
+ {
0.9999,
0.09999,
0.009999,
@@ -607,7 +606,7 @@ namespace Godot
0.00000009999,
0.000000009999,
};
- double abs = Mathf.Abs(step);
+ double abs = Abs(step);
double decs = abs - (int)abs; // Strip away integer part
for (int i = 0; i < sd.Length; i++)
{
@@ -620,25 +619,24 @@ namespace Godot
}
/// <summary>
- /// Snaps float value `s` to a given `step`.
- /// This can also be used to round a floating point
- /// number to an arbitrary number of decimals.
+ /// Snaps float value <paramref name="s"/> to a given <paramref name="step"/>.
+ /// This can also be used to round a floating point number to an arbitrary number of decimals.
/// </summary>
/// <param name="s">The value to snap.</param>
/// <param name="step">The step size to snap to.</param>
- /// <returns></returns>
+ /// <returns>The snapped value.</returns>
public static real_t Snapped(real_t s, real_t step)
{
if (step != 0f)
{
- return Floor(s / step + 0.5f) * step;
+ return Floor((s / step) + 0.5f) * step;
}
return s;
}
/// <summary>
- /// Returns the tangent of angle `s` in radians.
+ /// Returns the tangent of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The tangent of that angle.</returns>
@@ -648,7 +646,7 @@ namespace Godot
}
/// <summary>
- /// Returns the hyperbolic tangent of angle `s` in radians.
+ /// Returns the hyperbolic tangent of angle <paramref name="s"/> in radians.
/// </summary>
/// <param name="s">The angle in radians.</param>
/// <returns>The hyperbolic tangent of that angle.</returns>
@@ -658,8 +656,9 @@ namespace Godot
}
/// <summary>
- /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
- /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>.
+ /// Usable for creating loop-alike behavior or infinite surfaces.
+ /// If <paramref name="min"/> is <c>0</c>, this is equivalent
/// to <see cref="PosMod(int, int)"/>, so prefer using that instead.
/// </summary>
/// <param name="value">The value to wrap.</param>
@@ -669,12 +668,16 @@ namespace Godot
public static int Wrap(int value, int min, int max)
{
int range = max - min;
- return range == 0 ? min : min + ((value - min) % range + range) % range;
+ if (range == 0)
+ return min;
+
+ return min + ((((value - min) % range) + range) % range);
}
/// <summary>
- /// Wraps `value` between `min` and `max`. Usable for creating loop-alike
- /// behavior or infinite surfaces. If `min` is `0`, this is equivalent
+ /// Wraps <paramref name="value"/> between <paramref name="min"/> and <paramref name="max"/>.
+ /// Usable for creating loop-alike behavior or infinite surfaces.
+ /// If <paramref name="min"/> is <c>0</c>, this is equivalent
/// to <see cref="PosMod(real_t, real_t)"/>, so prefer using that instead.
/// </summary>
/// <param name="value">The value to wrap.</param>
@@ -684,7 +687,29 @@ namespace Godot
public static real_t Wrap(real_t value, real_t min, real_t max)
{
real_t range = max - min;
- return IsZeroApprox(range) ? min : min + ((value - min) % range + range) % range;
+ if (IsZeroApprox(range))
+ {
+ return min;
+ }
+ return min + ((((value - min) % range) + range) % range);
+ }
+
+ private static real_t Fract(real_t value)
+ {
+ return value - (real_t)Math.Floor(value);
+ }
+
+ /// <summary>
+ /// Returns the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code].
+ /// If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave).
+ /// If [code]length[/code] is less than zero, it becomes positive.
+ /// </summary>
+ /// <param name="value">The value to pingpong.</param>
+ /// <param name="length">The maximum value of the function.</param>
+ /// <returns>The ping-ponged value.</returns>
+ public static real_t PingPong(real_t value, real_t length)
+ {
+ return (length != (real_t)0.0) ? Abs(Fract((value - length) / (length * (real_t)2.0)) * length * (real_t)2.0 - length) : (real_t)0.0;
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
index 0888e33090..f15d01b34b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -12,7 +12,7 @@ namespace Godot
// Define constants with Decimal precision and cast down to double or float.
/// <summary>
- /// The natural number `e`.
+ /// The natural number <c>e</c>.
/// </summary>
public const real_t E = (real_t)2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
@@ -23,7 +23,7 @@ namespace Godot
/// <summary>
/// A very small number used for float comparison with error tolerance.
- /// 1e-06 with single-precision floats, but 1e-14 if `REAL_T_IS_DOUBLE`.
+ /// 1e-06 with single-precision floats, but 1e-14 if <c>REAL_T_IS_DOUBLE</c>.
/// </summary>
#if REAL_T_IS_DOUBLE
public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
@@ -44,7 +44,7 @@ namespace Godot
/// <summary>
/// Returns the amount of digits after the decimal place.
/// </summary>
- /// <param name="s">The input <see cref="System.Decimal"/> value.</param>
+ /// <param name="s">The input <see langword="decimal"/> value.</param>
/// <returns>The amount of digits.</returns>
public static int DecimalCount(decimal s)
{
@@ -52,48 +52,51 @@ namespace Godot
}
/// <summary>
- /// Rounds `s` upward (towards positive infinity).
+ /// Rounds <paramref name="s"/> upward (towards positive infinity).
///
- /// This is the same as <see cref="Ceil(real_t)"/>, but returns an `int`.
+ /// This is the same as <see cref="Ceil(real_t)"/>, but returns an <see langword="int"/>.
/// </summary>
/// <param name="s">The number to ceil.</param>
- /// <returns>The smallest whole number that is not less than `s`.</returns>
+ /// <returns>The smallest whole number that is not less than <paramref name="s"/>.</returns>
public static int CeilToInt(real_t s)
{
return (int)Math.Ceiling(s);
}
/// <summary>
- /// Rounds `s` downward (towards negative infinity).
+ /// Rounds <paramref name="s"/> downward (towards negative infinity).
///
- /// This is the same as <see cref="Floor(real_t)"/>, but returns an `int`.
+ /// This is the same as <see cref="Floor(real_t)"/>, but returns an <see langword="int"/>.
/// </summary>
/// <param name="s">The number to floor.</param>
- /// <returns>The largest whole number that is not more than `s`.</returns>
+ /// <returns>The largest whole number that is not more than <paramref name="s"/>.</returns>
public static int FloorToInt(real_t s)
{
return (int)Math.Floor(s);
}
/// <summary>
+ /// Rounds <paramref name="s"/> to the nearest whole number.
///
+ /// This is the same as <see cref="Round(real_t)"/>, but returns an <see langword="int"/>.
/// </summary>
- /// <param name="s"></param>
- /// <returns></returns>
+ /// <param name="s">The number to round.</param>
+ /// <returns>The rounded number.</returns>
public static int RoundToInt(real_t s)
{
return (int)Math.Round(s);
}
/// <summary>
- /// Returns true if `a` and `b` are approximately equal to each other.
+ /// Returns <see langword="true"/> if <paramref name="a"/> and <paramref name="b"/> are approximately
+ /// equal to each other.
/// The comparison is done using the provided tolerance value.
/// If you want the tolerance to be calculated for you, use <see cref="IsEqualApprox(real_t, real_t)"/>.
/// </summary>
/// <param name="a">One of the values.</param>
/// <param name="b">The other value.</param>
/// <param name="tolerance">The pre-calculated tolerance value.</param>
- /// <returns>A bool for whether or not the two values are equal.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the two values are equal.</returns>
public static bool IsEqualApprox(real_t a, real_t b, real_t tolerance)
{
// Check for exact equality first, required to handle "infinity" values.
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
index 4ecc55f94e..40fb5f8788 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs
@@ -3,9 +3,45 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// A pre-parsed relative or absolute path in a scene tree,
+ /// for use with <see cref="Node.GetNode(NodePath)"/> and similar functions.
+ /// It can reference a node, a resource within a node, or a property
+ /// of a node or resource.
+ /// For instance, <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c>
+ /// would refer to the <c>size</c> property of the <c>texture</c>
+ /// resource on the node named <c>"Sprite2D"</c> which is a child of
+ /// the other named nodes in the path.
+ /// You will usually just pass a string to <see cref="Node.GetNode(NodePath)"/>
+ /// and it will be automatically converted, but you may occasionally
+ /// want to parse a path ahead of time with NodePath.
+ /// Exporting a NodePath variable will give you a node selection widget
+ /// in the properties panel of the editor, which can often be useful.
+ /// A NodePath is composed of a list of slash-separated node names
+ /// (like a filesystem path) and an optional colon-separated list of
+ /// "subnames" which can be resources or properties.
+ ///
+ /// Note: In the editor, NodePath properties are automatically updated when moving,
+ /// renaming or deleting a node in the scene tree, but they are never updated at runtime.
+ /// </summary>
+ /// <example>
+ /// Some examples of NodePaths include the following:
+ /// <code>
+ /// // No leading slash means it is relative to the current node.
+ /// new NodePath("A"); // Immediate child A.
+ /// new NodePath("A/B"); // A's child B.
+ /// new NodePath("."); // The current node.
+ /// new NodePath(".."); // The parent node.
+ /// new NodePath("../C"); // A sibling node C.
+ /// // A leading slash means it is absolute from the SceneTree.
+ /// new NodePath("/root"); // Equivalent to GetTree().Root
+ /// new NodePath("/root/Main"); // If your main scene's root node were named "Main".
+ /// new NodePath("/root/MyAutoload"); // If you have an autoloaded node or scene.
+ /// </code>
+ /// </example>
public sealed partial class NodePath : IDisposable
{
- private bool disposed = false;
+ private bool _disposed = false;
private IntPtr ptr;
@@ -14,7 +50,7 @@ namespace Godot
if (instance == null)
throw new NullReferenceException($"The instance of type {nameof(NodePath)} is null.");
- if (instance.disposed)
+ if (instance._disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
@@ -25,6 +61,9 @@ namespace Godot
Dispose(false);
}
+ /// <summary>
+ /// Disposes of this <see cref="NodePath"/>.
+ /// </summary>
public void Dispose()
{
Dispose(true);
@@ -33,7 +72,7 @@ namespace Godot
private void Dispose(bool disposing)
{
- if (disposed)
+ if (_disposed)
return;
if (ptr != IntPtr.Zero)
@@ -42,7 +81,7 @@ namespace Godot
ptr = IntPtr.Zero;
}
- disposed = true;
+ _disposed = true;
}
internal NodePath(IntPtr ptr)
@@ -50,57 +89,168 @@ namespace Godot
this.ptr = ptr;
}
- public NodePath() : this(string.Empty) {}
-
+ /// <summary>
+ /// Constructs an empty <see cref="NodePath"/>.
+ /// </summary>
+ public NodePath() : this(string.Empty) { }
+
+ /// <summary>
+ /// Constructs a <see cref="NodePath"/> from a string <paramref name="path"/>,
+ /// e.g.: <c>"Path2D/PathFollow2D/Sprite2D:texture:size"</c>.
+ /// A path is absolute if it starts with a slash. Absolute paths
+ /// are only valid in the global scene tree, not within individual
+ /// scenes. In a relative path, <c>"."</c> and <c>".."</c> indicate
+ /// the current node and its parent.
+ /// The "subnames" optionally included after the path to the target
+ /// node can point to resources or properties, and can also be nested.
+ /// </summary>
+ /// <example>
+ /// Examples of valid NodePaths (assuming that those nodes exist and
+ /// have the referenced resources or properties):
+ /// <code>
+ /// // Points to the Sprite2D node.
+ /// "Path2D/PathFollow2D/Sprite2D"
+ /// // Points to the Sprite2D node and its "texture" resource.
+ /// // GetNode() would retrieve "Sprite2D", while GetNodeAndResource()
+ /// // would retrieve both the Sprite2D node and the "texture" resource.
+ /// "Path2D/PathFollow2D/Sprite2D:texture"
+ /// // Points to the Sprite2D node and its "position" property.
+ /// "Path2D/PathFollow2D/Sprite2D:position"
+ /// // Points to the Sprite2D node and the "x" component of its "position" property.
+ /// "Path2D/PathFollow2D/Sprite2D:position:x"
+ /// // Absolute path (from "root")
+ /// "/root/Level/Path2D"
+ /// </code>
+ /// </example>
+ /// <param name="path">A string that represents a path in a scene tree.</param>
public NodePath(string path)
{
ptr = godot_icall_NodePath_Ctor(path);
}
+ /// <summary>
+ /// Converts a string to a <see cref="NodePath"/>.
+ /// </summary>
+ /// <param name="from">The string to convert.</param>
public static implicit operator NodePath(string from) => new NodePath(from);
+ /// <summary>
+ /// Converts this <see cref="NodePath"/> to a string.
+ /// </summary>
+ /// <param name="from">The <see cref="NodePath"/> to convert.</param>
public static implicit operator string(NodePath from) => from.ToString();
+ /// <summary>
+ /// Converts this <see cref="NodePath"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this <see cref="NodePath"/>.</returns>
public override string ToString()
{
return godot_icall_NodePath_operator_String(GetPtr(this));
}
+ /// <summary>
+ /// Returns a node path with a colon character (<c>:</c>) prepended,
+ /// transforming it to a pure property path with no node name (defaults
+ /// to resolving from the current node).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// // This will be parsed as a node path to the "x" property in the "position" node.
+ /// var nodePath = new NodePath("position:x");
+ /// // This will be parsed as a node path to the "x" component of the "position" property in the current node.
+ /// NodePath propertyPath = nodePath.GetAsPropertyPath();
+ /// GD.Print(propertyPath); // :position:x
+ /// </code>
+ /// </example>
+ /// <returns>The <see cref="NodePath"/> as a pure property path.</returns>
public NodePath GetAsPropertyPath()
{
return new NodePath(godot_icall_NodePath_get_as_property_path(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>
+ /// <example>
+ /// <code>
+ /// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path");
+ /// GD.Print(nodepath.GetConcatenatedSubnames()); // texture:load_path
+ /// </code>
+ /// </example>
+ /// <returns>The subnames concatenated with <c>:</c>.</returns>
public string GetConcatenatedSubnames()
{
return godot_icall_NodePath_get_concatenated_subnames(GetPtr(this));
}
+ /// <summary>
+ /// Gets the node name indicated by <paramref name="idx"/> (0 to <see cref="GetNameCount"/>).
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// var nodePath = new NodePath("Path2D/PathFollow2D/Sprite2D");
+ /// GD.Print(nodePath.GetName(0)); // Path2D
+ /// GD.Print(nodePath.GetName(1)); // PathFollow2D
+ /// GD.Print(nodePath.GetName(2)); // Sprite
+ /// </code>
+ /// </example>
+ /// <param name="idx">The name index.</param>
+ /// <returns>The name at the given index <paramref name="idx"/>.</returns>
public string GetName(int idx)
{
return godot_icall_NodePath_get_name(GetPtr(this), idx);
}
+ /// <summary>
+ /// Gets the number of node names which make up the path.
+ /// Subnames (see <see cref="GetSubnameCount"/>) are not included.
+ /// For example, <c>"Path2D/PathFollow2D/Sprite2D"</c> has 3 names.
+ /// </summary>
+ /// <returns>The number of node names which make up the path.</returns>
public int GetNameCount()
{
return godot_icall_NodePath_get_name_count(GetPtr(this));
}
+ /// <summary>
+ /// Gets the resource or property name indicated by <paramref name="idx"/> (0 to <see cref="GetSubnameCount"/>).
+ /// </summary>
+ /// <param name="idx">The subname index.</param>
+ /// <returns>The subname at the given index <paramref name="idx"/>.</returns>
public string GetSubname(int idx)
{
return godot_icall_NodePath_get_subname(GetPtr(this), idx);
}
+ /// <summary>
+ /// Gets the number of resource or property names ("subnames") in the path.
+ /// Each subname is listed after a colon character (<c>:</c>) in the node path.
+ /// For example, <c>"Path2D/PathFollow2D/Sprite2D:texture:load_path"</c> has 2 subnames.
+ /// </summary>
+ /// <returns>The number of subnames in the path.</returns>
public int GetSubnameCount()
{
return godot_icall_NodePath_get_subname_count(GetPtr(this));
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the node path is absolute (as opposed to relative),
+ /// which means that it starts with a slash character (<c>/</c>). Absolute node paths can
+ /// be used to access the root node (<c>"/root"</c>) or autoloads (e.g. <c>"/global"</c>
+ /// if a "global" autoload was registered).
+ /// </summary>
+ /// <returns>If the <see cref="NodePath"/> is an absolute path.</returns>
public bool IsAbsolute()
{
return godot_icall_NodePath_is_absolute(GetPtr(this));
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the node path is empty.
+ /// </summary>
+ /// <returns>If the <see cref="NodePath"/> is empty.</returns>
public bool IsEmpty()
{
return godot_icall_NodePath_is_empty(GetPtr(this));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
index d486d79557..746612477d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -5,13 +5,16 @@ namespace Godot
{
public partial class Object : IDisposable
{
- private bool disposed = false;
+ private bool _disposed = false;
private static StringName nativeName = "Object";
internal IntPtr ptr;
internal bool memoryOwn;
+ /// <summary>
+ /// Constructs a new <see cref="Object"/>.
+ /// </summary>
public Object() : this(false)
{
if (ptr == IntPtr.Zero)
@@ -29,6 +32,9 @@ namespace Godot
this.memoryOwn = memoryOwn;
}
+ /// <summary>
+ /// The pointer to the native instance of this <see cref="Object"/>.
+ /// </summary>
public IntPtr NativeInstance
{
get { return ptr; }
@@ -39,7 +45,7 @@ namespace Godot
if (instance == null)
return IntPtr.Zero;
- if (instance.disposed)
+ if (instance._disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
@@ -50,15 +56,21 @@ namespace Godot
Dispose(false);
}
+ /// <summary>
+ /// Disposes of this <see cref="Object"/>.
+ /// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
+ /// <summary>
+ /// Disposes implementation of this <see cref="Object"/>.
+ /// </summary>
protected virtual void Dispose(bool disposing)
{
- if (disposed)
+ if (_disposed)
return;
if (ptr != IntPtr.Zero)
@@ -73,19 +85,23 @@ namespace Godot
godot_icall_Object_Disposed(this, ptr);
}
- this.ptr = IntPtr.Zero;
+ ptr = IntPtr.Zero;
}
- disposed = true;
+ _disposed = true;
}
+ /// <summary>
+ /// Converts this <see cref="Object"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this object.</returns>
public override string ToString()
{
return godot_icall_Object_ToString(GetPtr(this));
}
/// <summary>
- /// Returns a new <see cref="Godot.SignalAwaiter"/> awaiter configured to complete when the instance
+ /// Returns a new <see cref="SignalAwaiter"/> awaiter configured to complete when the instance
/// <paramref name="source"/> emits the signal specified by the <paramref name="signal"/> parameter.
/// </summary>
/// <param name="source">
@@ -101,19 +117,23 @@ namespace Godot
/// {
/// for (int i = 0; i &lt; 100; i++)
/// {
- /// await ToSignal(GetTree(), "idle_frame");
+ /// await ToSignal(GetTree(), "process_frame");
/// GD.Print($"Frame {i}");
/// }
/// }
/// </code>
/// </example>
+ /// <returns>
+ /// A <see cref="SignalAwaiter"/> that completes when
+ /// <paramref name="source"/> emits the <paramref name="signal"/>.
+ /// </returns>
public SignalAwaiter ToSignal(Object source, StringName signal)
{
return new SignalAwaiter(source, signal, this);
}
/// <summary>
- /// Gets a new <see cref="Godot.DynamicGodotObject"/> associated with this instance.
+ /// Gets a new <see cref="DynamicGodotObject"/> associated with this instance.
/// </summary>
public dynamic DynamicObject => new DynamicGodotObject(this);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 6972102730..63af1c5892 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -21,10 +21,10 @@ namespace Godot
/// <summary>
/// The normal of the plane, which must be normalized.
- /// In the scalar equation of the plane `ax + by + cz = d`, this is
- /// the vector `(a, b, c)`, where `d` is the <see cref="D"/> property.
+ /// In the scalar equation of the plane <c>ax + by + cz = d</c>, this is
+ /// the vector <c>(a, b, c)</c>, where <c>d</c> is the <see cref="D"/> property.
/// </summary>
- /// <value>Equivalent to `x`, `y`, and `z`.</value>
+ /// <value>Equivalent to <see cref="x"/>, <see cref="y"/>, and <see cref="z"/>.</value>
public Vector3 Normal
{
get { return _normal; }
@@ -82,8 +82,8 @@ namespace Godot
/// <summary>
/// The distance from the origin to the plane (in the direction of
/// <see cref="Normal"/>). This value is typically non-negative.
- /// In the scalar equation of the plane `ax + by + cz = d`,
- /// this is `d`, while the `(a, b, c)` coordinates are represented
+ /// In the scalar equation of the plane <c>ax + by + cz = d</c>,
+ /// this is <c>d</c>, while the <c>(a, b, c)</c> coordinates are represented
/// by the <see cref="Normal"/> property.
/// </summary>
/// <value>The plane's distance from the origin.</value>
@@ -92,7 +92,7 @@ namespace Godot
/// <summary>
/// The center of the plane, the point where the normal line intersects the plane.
/// </summary>
- /// <value>Equivalent to <see cref="Normal"/> multiplied by `D`.</value>
+ /// <value>Equivalent to <see cref="Normal"/> multiplied by <see cref="D"/>.</value>
public Vector3 Center
{
get
@@ -107,7 +107,7 @@ namespace Godot
}
/// <summary>
- /// Returns the shortest distance from this plane to the position `point`.
+ /// Returns the shortest distance from this plane to the position <paramref name="point"/>.
/// </summary>
/// <param name="point">The position to use for the calculation.</param>
/// <returns>The shortest distance.</returns>
@@ -117,12 +117,12 @@ namespace Godot
}
/// <summary>
- /// Returns true if point is inside the plane.
+ /// Returns <see langword="true"/> if point is inside the plane.
/// Comparison uses a custom minimum epsilon threshold.
/// </summary>
/// <param name="point">The point to check.</param>
/// <param name="epsilon">The tolerance threshold.</param>
- /// <returns>A bool for whether or not the plane has the point.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the plane has the point.</returns>
public bool HasPoint(Vector3 point, real_t epsilon = Mathf.Epsilon)
{
real_t dist = _normal.Dot(point) - D;
@@ -130,12 +130,12 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection point of the three planes: `b`, `c`,
- /// and this plane. If no intersection is found, `null` is returned.
+ /// Returns the intersection point of the three planes: <paramref name="b"/>, <paramref name="c"/>,
+ /// and this plane. If no intersection is found, <see langword="null"/> is returned.
/// </summary>
/// <param name="b">One of the three planes to use in the calculation.</param>
/// <param name="c">One of the three planes to use in the calculation.</param>
- /// <returns>The intersection, or `null` if none is found.</returns>
+ /// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
public Vector3? Intersect3(Plane b, Plane c)
{
real_t denom = _normal.Cross(b._normal).Dot(c._normal);
@@ -145,21 +145,21 @@ namespace Godot
return null;
}
- Vector3 result = b._normal.Cross(c._normal) * D +
- c._normal.Cross(_normal) * b.D +
- _normal.Cross(b._normal) * c.D;
+ Vector3 result = (b._normal.Cross(c._normal) * D) +
+ (c._normal.Cross(_normal) * b.D) +
+ (_normal.Cross(b._normal) * c.D);
return result / denom;
}
/// <summary>
- /// Returns the intersection point of a ray consisting of the
- /// position `from` and the direction normal `dir` with this plane.
- /// If no intersection is found, `null` is returned.
+ /// Returns the intersection point of a ray consisting of the position <paramref name="from"/>
+ /// and the direction normal <paramref name="dir"/> with this plane.
+ /// If no intersection is found, <see langword="null"/> is returned.
/// </summary>
/// <param name="from">The start of the ray.</param>
/// <param name="dir">The direction of the ray, normalized.</param>
- /// <returns>The intersection, or `null` if none is found.</returns>
+ /// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
public Vector3? IntersectRay(Vector3 from, Vector3 dir)
{
real_t den = _normal.Dot(dir);
@@ -182,12 +182,12 @@ namespace Godot
/// <summary>
/// Returns the intersection point of a line segment from
- /// position `begin` to position `end` with this plane.
- /// If no intersection is found, `null` is returned.
+ /// position <paramref name="begin"/> to position <paramref name="end"/> with this plane.
+ /// If no intersection is found, <see langword="null"/> is returned.
/// </summary>
/// <param name="begin">The start of the line segment.</param>
/// <param name="end">The end of the line segment.</param>
- /// <returns>The intersection, or `null` if none is found.</returns>
+ /// <returns>The intersection, or <see langword="null"/> if none is found.</returns>
public Vector3? IntersectSegment(Vector3 begin, Vector3 end)
{
Vector3 segment = begin - end;
@@ -210,10 +210,10 @@ namespace Godot
}
/// <summary>
- /// Returns true if `point` is located above the plane.
+ /// Returns <see langword="true"/> if <paramref name="point"/> is located above the plane.
/// </summary>
/// <param name="point">The point to check.</param>
- /// <returns>A bool for whether or not the point is above the plane.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not the point is above the plane.</returns>
public bool IsPointOver(Vector3 point)
{
return _normal.Dot(point) > D;
@@ -236,13 +236,13 @@ namespace Godot
}
/// <summary>
- /// Returns the orthogonal projection of `point` into the plane.
+ /// Returns the orthogonal projection of <paramref name="point"/> into the plane.
/// </summary>
/// <param name="point">The point to project.</param>
/// <returns>The projected point.</returns>
public Vector3 Project(Vector3 point)
{
- return point - _normal * DistanceTo(point);
+ return point - (_normal * DistanceTo(point));
}
// Constants
@@ -251,27 +251,28 @@ namespace Godot
private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
/// <summary>
- /// A plane that extends in the Y and Z axes (normal vector points +X).
+ /// A <see cref="Plane"/> that extends in the Y and Z axes (normal vector points +X).
/// </summary>
- /// <value>Equivalent to `new Plane(1, 0, 0, 0)`.</value>
+ /// <value>Equivalent to <c>new Plane(1, 0, 0, 0)</c>.</value>
public static Plane PlaneYZ { get { return _planeYZ; } }
/// <summary>
- /// A plane that extends in the X and Z axes (normal vector points +Y).
+ /// A <see cref="Plane"/> that extends in the X and Z axes (normal vector points +Y).
/// </summary>
- /// <value>Equivalent to `new Plane(0, 1, 0, 0)`.</value>
+ /// <value>Equivalent to <c>new Plane(0, 1, 0, 0)</c>.</value>
public static Plane PlaneXZ { get { return _planeXZ; } }
/// <summary>
- /// A plane that extends in the X and Y axes (normal vector points +Z).
+ /// A <see cref="Plane"/> that extends in the X and Y axes (normal vector points +Z).
/// </summary>
- /// <value>Equivalent to `new Plane(0, 0, 1, 0)`.</value>
+ /// <value>Equivalent to <c>new Plane(0, 0, 1, 0)</c>.</value>
public static Plane PlaneXY { get { return _planeXY; } }
/// <summary>
- /// Constructs a plane from four values. `a`, `b` and `c` become the
+ /// Constructs a <see cref="Plane"/> from four values.
+ /// <paramref name="a"/>, <paramref name="b"/> and <paramref name="c"/> become the
/// components of the resulting plane's <see cref="Normal"/> vector.
- /// `d` becomes the plane's distance from the origin.
+ /// <paramref name="d"/> becomes the plane's distance from the origin.
/// </summary>
/// <param name="a">The X component of the plane's normal vector.</param>
/// <param name="b">The Y component of the plane's normal vector.</param>
@@ -280,22 +281,23 @@ namespace Godot
public Plane(real_t a, real_t b, real_t c, real_t d)
{
_normal = new Vector3(a, b, c);
- this.D = d;
+ D = d;
}
/// <summary>
- /// Constructs a plane from a normal vector and the plane's distance to the origin.
+ /// Constructs a <see cref="Plane"/> from a <paramref name="normal"/> vector and
+ /// the plane's distance to the origin <paramref name="d"/>.
/// </summary>
/// <param name="normal">The normal of the plane, must be normalized.</param>
/// <param name="d">The plane's distance from the origin. This value is typically non-negative.</param>
public Plane(Vector3 normal, real_t d)
{
- this._normal = normal;
- this.D = d;
+ _normal = normal;
+ D = d;
}
/// <summary>
- /// Constructs a plane from the three points, given in clockwise order.
+ /// Constructs a <see cref="Plane"/> from the three points, given in clockwise order.
/// </summary>
/// <param name="v1">The first point.</param>
/// <param name="v2">The second point.</param>
@@ -307,21 +309,53 @@ namespace Godot
D = _normal.Dot(v1);
}
+ /// <summary>
+ /// Returns the negative value of the <see cref="Plane"/>.
+ /// This is the same as writing <c>new Plane(-p.Normal, -p.D)</c>.
+ /// This operation flips the direction of the normal vector and
+ /// also flips the distance value, resulting in a Plane that is
+ /// in the same place, but facing the opposite direction.
+ /// </summary>
+ /// <param name="plane">The plane to negate/flip.</param>
+ /// <returns>The negated/flipped plane.</returns>
public static Plane operator -(Plane plane)
{
return new Plane(-plane._normal, -plane.D);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Plane"/>s are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the planes are exactly equal.</returns>
public static bool operator ==(Plane left, Plane right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Plane"/>s are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the planes are not equal.</returns>
public static bool operator !=(Plane left, Plane right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this plane and <paramref name="obj"/> are equal.
+ /// </summary>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns>Whether or not the plane and the other object are exactly equal.</returns>
public override bool Equals(object obj)
{
if (obj is Plane)
@@ -332,14 +366,19 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this plane and <paramref name="other"/> are equal.
+ /// </summary>
+ /// <param name="other">The other plane to compare.</param>
+ /// <returns>Whether or not the planes are exactly equal.</returns>
public bool Equals(Plane other)
{
return _normal == other._normal && D == other.D;
}
/// <summary>
- /// Returns true if this plane and `other` are approximately equal, by running
- /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// Returns <see langword="true"/> if this plane and <paramref name="other"/> are
+ /// approximately equal, by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
/// </summary>
/// <param name="other">The other plane to compare.</param>
/// <returns>Whether or not the planes are approximately equal.</returns>
@@ -348,16 +387,28 @@ namespace Godot
return _normal.IsEqualApprox(other._normal) && Mathf.IsEqualApprox(D, other.D);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Plane"/>.
+ /// </summary>
+ /// <returns>A hash code for this plane.</returns>
public override int GetHashCode()
{
return _normal.GetHashCode() ^ D.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Plane"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this plane.</returns>
public override string ToString()
{
return $"{_normal}, {D}";
}
+ /// <summary>
+ /// Converts this <see cref="Plane"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this plane.</returns>
public string ToString(string format)
{
return $"{_normal.ToString(format)}, {D.ToString(format)}";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
index 0fed55cc30..e38dca414f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -12,10 +12,10 @@ namespace Godot
/// A unit quaternion used for representing 3D rotations.
/// Quaternions need to be normalized to be used for rotation.
///
- /// It is similar to Basis, which implements matrix representation of
- /// rotations, and can be parametrized using both an axis-angle pair
- /// or Euler angles. Basis stores rotation, scale, and shearing,
- /// while Quaternion only stores rotation.
+ /// It is similar to <see cref="Basis"/>, which implements matrix
+ /// representation of rotations, and can be parametrized using both
+ /// an axis-angle pair or Euler angles. Basis stores rotation, scale,
+ /// and shearing, while Quaternion only stores rotation.
///
/// Due to its compactness and the way it is stored in memory, certain
/// operations (obtaining axis-angle and performing SLERP, in particular)
@@ -26,19 +26,19 @@ namespace Godot
public struct Quaternion : IEquatable<Quaternion>
{
/// <summary>
- /// X component of the quaternion (imaginary `i` axis part).
+ /// X component of the quaternion (imaginary <c>i</c> axis part).
/// Quaternion components should usually not be manipulated directly.
/// </summary>
public real_t x;
/// <summary>
- /// Y component of the quaternion (imaginary `j` axis part).
+ /// Y component of the quaternion (imaginary <c>j</c> axis part).
/// Quaternion components should usually not be manipulated directly.
/// </summary>
public real_t y;
/// <summary>
- /// Z component of the quaternion (imaginary `k` axis part).
+ /// Z component of the quaternion (imaginary <c>k</c> axis part).
/// Quaternion components should usually not be manipulated directly.
/// </summary>
public real_t z;
@@ -52,7 +52,12 @@ namespace Godot
/// <summary>
/// Access quaternion components using their index.
/// </summary>
- /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`, `[3]` is equivalent to `.w`.</value>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>,
+ /// <c>[2]</c> is equivalent to <see cref="z"/>,
+ /// <c>[3]</c> is equivalent to <see cref="w"/>.
+ /// </value>
public real_t this[int index]
{
get
@@ -96,7 +101,8 @@ namespace Godot
/// <summary>
/// Returns the length (magnitude) of the quaternion.
/// </summary>
- /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value>
+ /// <seealso cref="LengthSquared"/>
+ /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
public real_t Length
{
get { return Mathf.Sqrt(LengthSquared); }
@@ -107,14 +113,14 @@ namespace Godot
/// This method runs faster than <see cref="Length"/>, so prefer it if
/// you need to compare quaternions or need the squared length for some formula.
/// </summary>
- /// <value>Equivalent to `Dot(this)`.</value>
+ /// <value>Equivalent to <c>Dot(this)</c>.</value>
public real_t LengthSquared
{
get { return Dot(this); }
}
/// <summary>
- /// Returns the angle between this quaternion and `to`.
+ /// Returns the angle between this quaternion and <paramref name="to"/>.
/// This is the magnitude of the angle you would need to rotate
/// by to get from one to the other.
///
@@ -131,12 +137,12 @@ namespace Godot
}
/// <summary>
- /// Performs a cubic spherical interpolation between quaternions `preA`,
- /// this vector, `b`, and `postB`, by the given amount `t`.
+ /// Performs a cubic spherical interpolation between quaternions <paramref name="preA"/>, this quaternion,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
/// </summary>
/// <param name="b">The destination quaternion.</param>
/// <param name="preA">A quaternion before this quaternion.</param>
- /// <param name="postB">A quaternion after `b`.</param>
+ /// <param name="postB">A quaternion after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated quaternion.</returns>
public Quaternion CubicSlerp(Quaternion b, Quaternion preA, Quaternion postB, real_t weight)
@@ -154,7 +160,7 @@ namespace Godot
/// <returns>The dot product.</returns>
public real_t Dot(Quaternion b)
{
- return x * b.x + y * b.y + z * b.z + w * b.w;
+ return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w);
}
/// <summary>
@@ -194,7 +200,7 @@ namespace Godot
/// <summary>
/// Returns whether the quaternion is normalized or not.
/// </summary>
- /// <returns>A bool for whether the quaternion is normalized or not.</returns>
+ /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
@@ -211,7 +217,7 @@ namespace Godot
/// <summary>
/// Returns the result of the spherical linear interpolation between
- /// this quaternion and `to` by amount `weight`.
+ /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>.
///
/// Note: Both quaternions must be normalized.
/// </summary>
@@ -274,16 +280,16 @@ namespace Godot
// Calculate final values.
return new Quaternion
(
- scale0 * x + scale1 * to1.x,
- scale0 * y + scale1 * to1.y,
- scale0 * z + scale1 * to1.z,
- scale0 * w + scale1 * to1.w
+ (scale0 * x) + (scale1 * to1.x),
+ (scale0 * y) + (scale1 * to1.y),
+ (scale0 * z) + (scale1 * to1.z),
+ (scale0 * w) + (scale1 * to1.w)
);
}
/// <summary>
/// Returns the result of the spherical linear interpolation between
- /// this quaternion and `to` by amount `weight`, but without
+ /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>, but without
/// checking if the rotation path is not bigger than 90 degrees.
/// </summary>
/// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
@@ -305,10 +311,10 @@ namespace Godot
return new Quaternion
(
- invFactor * x + newFactor * to.x,
- invFactor * y + newFactor * to.y,
- invFactor * z + newFactor * to.z,
- invFactor * w + newFactor * to.w
+ (invFactor * x) + (newFactor * to.x),
+ (invFactor * y) + (newFactor * to.y),
+ (invFactor * z) + (newFactor * to.z),
+ (invFactor * w) + (newFactor * to.w)
);
}
@@ -327,7 +333,7 @@ namespace Godot
#endif
var u = new Vector3(x, y, z);
Vector3 uv = u.Cross(v);
- return v + ((uv * w) + u.Cross(uv)) * 2;
+ return v + (((uv * w) + u.Cross(uv)) * 2);
}
// Constants
@@ -338,15 +344,15 @@ namespace Godot
/// Equivalent to an identity <see cref="Basis"/> matrix. If a vector is transformed by
/// an identity quaternion, it will not change.
/// </summary>
- /// <value>Equivalent to `new Quaternion(0, 0, 0, 1)`.</value>
+ /// <value>Equivalent to <c>new Quaternion(0, 0, 0, 1)</c>.</value>
public static Quaternion Identity { get { return _identity; } }
/// <summary>
- /// Constructs a quaternion defined by the given values.
+ /// Constructs a <see cref="Quaternion"/> defined by the given values.
/// </summary>
- /// <param name="x">X component of the quaternion (imaginary `i` axis part).</param>
- /// <param name="y">Y component of the quaternion (imaginary `j` axis part).</param>
- /// <param name="z">Z component of the quaternion (imaginary `k` axis part).</param>
+ /// <param name="x">X component of the quaternion (imaginary <c>i</c> axis part).</param>
+ /// <param name="y">Y component of the quaternion (imaginary <c>j</c> axis part).</param>
+ /// <param name="z">Z component of the quaternion (imaginary <c>k</c> axis part).</param>
/// <param name="w">W component of the quaternion (real part).</param>
public Quaternion(real_t x, real_t y, real_t z, real_t w)
{
@@ -357,7 +363,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a quaternion from the given quaternion.
+ /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>.
/// </summary>
/// <param name="q">The existing quaternion.</param>
public Quaternion(Quaternion q)
@@ -366,46 +372,45 @@ namespace Godot
}
/// <summary>
- /// Constructs a quaternion from the given <see cref="Basis"/>.
+ /// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
/// </summary>
- /// <param name="basis">The basis to construct from.</param>
+ /// <param name="basis">The <see cref="Basis"/> to construct from.</param>
public Quaternion(Basis basis)
{
- this = basis.Quaternion();
+ this = basis.GetQuaternion();
}
/// <summary>
- /// Constructs a quaternion that will perform a rotation specified by
- /// Euler angles (in the YXZ convention: when decomposing,
- /// first Z, then X, and Y last),
+ /// Constructs a <see cref="Quaternion"/> that will perform a rotation specified by
+ /// Euler angles (in the YXZ convention: when decomposing, first Z, then X, and Y last),
/// given in the vector format as (X angle, Y angle, Z angle).
/// </summary>
- /// <param name="eulerYXZ"></param>
+ /// <param name="eulerYXZ">Euler angles that the quaternion will be rotated by.</param>
public Quaternion(Vector3 eulerYXZ)
{
- real_t half_a1 = eulerYXZ.y * 0.5f;
- real_t half_a2 = eulerYXZ.x * 0.5f;
- real_t half_a3 = eulerYXZ.z * 0.5f;
+ real_t halfA1 = eulerYXZ.y * 0.5f;
+ real_t halfA2 = eulerYXZ.x * 0.5f;
+ real_t halfA3 = eulerYXZ.z * 0.5f;
// R = Y(a1).X(a2).Z(a3) convention for Euler angles.
// Conversion to quaternion as listed in https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19770024290.pdf (page A-6)
// a3 is the angle of the first rotation, following the notation in this reference.
- real_t cos_a1 = Mathf.Cos(half_a1);
- real_t sin_a1 = Mathf.Sin(half_a1);
- real_t cos_a2 = Mathf.Cos(half_a2);
- real_t sin_a2 = Mathf.Sin(half_a2);
- real_t cos_a3 = Mathf.Cos(half_a3);
- real_t sin_a3 = Mathf.Sin(half_a3);
+ real_t cosA1 = Mathf.Cos(halfA1);
+ real_t sinA1 = Mathf.Sin(halfA1);
+ real_t cosA2 = Mathf.Cos(halfA2);
+ real_t sinA2 = Mathf.Sin(halfA2);
+ real_t cosA3 = Mathf.Cos(halfA3);
+ real_t sinA3 = Mathf.Sin(halfA3);
- x = sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3;
- y = sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3;
- z = cos_a1 * cos_a2 * sin_a3 - sin_a1 * sin_a2 * cos_a3;
- w = sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3;
+ x = (sinA1 * cosA2 * sinA3) + (cosA1 * sinA2 * cosA3);
+ y = (sinA1 * cosA2 * cosA3) - (cosA1 * sinA2 * sinA3);
+ z = (cosA1 * cosA2 * sinA3) - (sinA1 * sinA2 * cosA3);
+ w = (sinA1 * sinA2 * sinA3) + (cosA1 * cosA2 * cosA3);
}
/// <summary>
- /// Constructs a quaternion that will rotate around the given axis
+ /// Constructs a <see cref="Quaternion"/> that will rotate around the given axis
/// by the specified angle. The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
@@ -441,79 +446,172 @@ namespace Godot
}
}
+ /// <summary>
+ /// Composes these two quaternions by multiplying them together.
+ /// This has the effect of rotating the second quaternion
+ /// (the child) by the first quaternion (the parent).
+ /// </summary>
+ /// <param name="left">The parent quaternion.</param>
+ /// <param name="right">The child quaternion.</param>
+ /// <returns>The composed quaternion.</returns>
public static Quaternion operator *(Quaternion left, Quaternion right)
{
return new Quaternion
(
- left.w * right.x + left.x * right.w + left.y * right.z - left.z * right.y,
- left.w * right.y + left.y * right.w + left.z * right.x - left.x * right.z,
- left.w * right.z + left.z * right.w + left.x * right.y - left.y * right.x,
- left.w * right.w - left.x * right.x - left.y * right.y - left.z * right.z
+ (left.w * right.x) + (left.x * right.w) + (left.y * right.z) - (left.z * right.y),
+ (left.w * right.y) + (left.y * right.w) + (left.z * right.x) - (left.x * right.z),
+ (left.w * right.z) + (left.z * right.w) + (left.x * right.y) - (left.y * right.x),
+ (left.w * right.w) - (left.x * right.x) - (left.y * right.y) - (left.z * right.z)
);
}
+ /// <summary>
+ /// Adds each component of the left <see cref="Quaternion"/>
+ /// to the right <see cref="Quaternion"/>. This operation is not
+ /// meaningful on its own, but it can be used as a part of a
+ /// larger expression, such as approximating an intermediate
+ /// rotation between two nearby rotations.
+ /// </summary>
+ /// <param name="left">The left quaternion to add.</param>
+ /// <param name="right">The right quaternion to add.</param>
+ /// <returns>The added quaternion.</returns>
public static Quaternion operator +(Quaternion left, Quaternion right)
{
return new Quaternion(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
}
+ /// <summary>
+ /// Subtracts each component of the left <see cref="Quaternion"/>
+ /// by the right <see cref="Quaternion"/>. This operation is not
+ /// meaningful on its own, but it can be used as a part of a
+ /// larger expression.
+ /// </summary>
+ /// <param name="left">The left quaternion to subtract.</param>
+ /// <param name="right">The right quaternion to subtract.</param>
+ /// <returns>The subtracted quaternion.</returns>
public static Quaternion operator -(Quaternion left, Quaternion right)
{
return new Quaternion(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
}
- public static Quaternion operator -(Quaternion left)
+ /// <summary>
+ /// Returns the negative value of the <see cref="Quaternion"/>.
+ /// This is the same as writing
+ /// <c>new Quaternion(-q.x, -q.y, -q.z, -q.w)</c>. This operation
+ /// results in a quaternion that represents the same rotation.
+ /// </summary>
+ /// <param name="quat">The quaternion to negate.</param>
+ /// <returns>The negated quaternion.</returns>
+ public static Quaternion operator -(Quaternion quat)
{
- return new Quaternion(-left.x, -left.y, -left.z, -left.w);
+ return new Quaternion(-quat.x, -quat.y, -quat.z, -quat.w);
}
- public static Quaternion operator *(Quaternion left, Vector3 right)
+ /// <summary>
+ /// Rotates (multiplies) the <see cref="Vector3"/>
+ /// by the given <see cref="Quaternion"/>.
+ /// </summary>
+ /// <param name="quat">The quaternion to rotate by.</param>
+ /// <param name="vec">The vector to rotate.</param>
+ /// <returns>The rotated vector.</returns>
+ public static Vector3 operator *(Quaternion quat, Vector3 vec)
{
- return new Quaternion
- (
- left.w * right.x + left.y * right.z - left.z * right.y,
- left.w * right.y + left.z * right.x - left.x * right.z,
- left.w * right.z + left.x * right.y - left.y * right.x,
- -left.x * right.x - left.y * right.y - left.z * right.z
- );
+#if DEBUG
+ if (!quat.IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized.");
+ }
+#endif
+ var u = new Vector3(quat.x, quat.y, quat.z);
+ Vector3 uv = u.Cross(vec);
+ return vec + (((uv * quat.w) + u.Cross(uv)) * 2);
}
- public static Quaternion operator *(Vector3 left, Quaternion right)
+ /// <summary>
+ /// Inversely rotates (multiplies) the <see cref="Vector3"/>
+ /// by the given <see cref="Quaternion"/>.
+ /// </summary>
+ /// <param name="vec">The vector to rotate.</param>
+ /// <param name="quat">The quaternion to rotate by.</param>
+ /// <returns>The inversely rotated vector.</returns>
+ public static Vector3 operator *(Vector3 vec, Quaternion quat)
{
- return new Quaternion
- (
- right.w * left.x + right.y * left.z - right.z * left.y,
- right.w * left.y + right.z * left.x - right.x * left.z,
- right.w * left.z + right.x * left.y - right.y * left.x,
- -right.x * left.x - right.y * left.y - right.z * left.z
- );
+ return quat.Inverse() * vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Quaternion"/>
+ /// by the given <see cref="real_t"/>. This operation is not
+ /// meaningful on its own, but it can be used as a part of a
+ /// larger expression.
+ /// </summary>
+ /// <param name="left">The quaternion to multiply.</param>
+ /// <param name="right">The value to multiply by.</param>
+ /// <returns>The multiplied quaternion.</returns>
public static Quaternion operator *(Quaternion left, real_t right)
{
return new Quaternion(left.x * right, left.y * right, left.z * right, left.w * right);
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Quaternion"/>
+ /// by the given <see cref="real_t"/>. This operation is not
+ /// meaningful on its own, but it can be used as a part of a
+ /// larger expression.
+ /// </summary>
+ /// <param name="left">The value to multiply by.</param>
+ /// <param name="right">The quaternion to multiply.</param>
+ /// <returns>The multiplied quaternion.</returns>
public static Quaternion operator *(real_t left, Quaternion right)
{
return new Quaternion(right.x * left, right.y * left, right.z * left, right.w * left);
}
+ /// <summary>
+ /// Divides each component of the <see cref="Quaternion"/>
+ /// by the given <see cref="real_t"/>. This operation is not
+ /// meaningful on its own, but it can be used as a part of a
+ /// larger expression.
+ /// </summary>
+ /// <param name="left">The quaternion to divide.</param>
+ /// <param name="right">The value to divide by.</param>
+ /// <returns>The divided quaternion.</returns>
public static Quaternion operator /(Quaternion left, real_t right)
{
return left * (1.0f / right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the quaternions are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left quaternion.</param>
+ /// <param name="right">The right quaternion.</param>
+ /// <returns>Whether or not the quaternions are exactly equal.</returns>
public static bool operator ==(Quaternion left, Quaternion right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the quaternions are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left quaternion.</param>
+ /// <param name="right">The right quaternion.</param>
+ /// <returns>Whether or not the quaternions are not equal.</returns>
public static bool operator !=(Quaternion left, Quaternion right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this quaternion and <paramref name="obj"/> are equal.
+ /// </summary>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns>Whether or not the quaternion and the other object are exactly equal.</returns>
public override bool Equals(object obj)
{
if (obj is Quaternion)
@@ -524,14 +622,19 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are equal.
+ /// </summary>
+ /// <param name="other">The other quaternion to compare.</param>
+ /// <returns>Whether or not the quaternions are exactly equal.</returns>
public bool Equals(Quaternion other)
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}
/// <summary>
- /// Returns true if this quaternion and `other` are approximately equal, by running
- /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// Returns <see langword="true"/> if this quaternion and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
/// </summary>
/// <param name="other">The other quaternion to compare.</param>
/// <returns>Whether or not the quaternions are approximately equal.</returns>
@@ -540,16 +643,28 @@ namespace Godot
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Quaternion"/>.
+ /// </summary>
+ /// <returns>A hash code for this quaternion.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Quaternion"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this quaternion.</returns>
public override string ToString()
{
return $"({x}, {y}, {z}, {w})";
}
+ /// <summary>
+ /// Converts this <see cref="Quaternion"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this quaternion.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)}, {w.ToString(format)})";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
index 94761531b1..1588869ec0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs
@@ -3,9 +3,15 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// The RID type is used to access the unique integer ID of a resource.
+ /// They are opaque, which means they do not grant access to the associated
+ /// resource by themselves. They are used by and with the low-level Server
+ /// classes such as <see cref="RenderingServer"/>.
+ /// </summary>
public sealed partial class RID : IDisposable
{
- private bool disposed = false;
+ private bool _disposed = false;
internal IntPtr ptr;
@@ -14,7 +20,7 @@ namespace Godot
if (instance == null)
throw new NullReferenceException($"The instance of type {nameof(RID)} is null.");
- if (instance.disposed)
+ if (instance._disposed)
throw new ObjectDisposedException(instance.GetType().FullName);
return instance.ptr;
@@ -25,6 +31,9 @@ namespace Godot
Dispose(false);
}
+ /// <summary>
+ /// Disposes of this <see cref="RID"/>.
+ /// </summary>
public void Dispose()
{
Dispose(true);
@@ -33,7 +42,7 @@ namespace Godot
private void Dispose(bool disposing)
{
- if (disposed)
+ if (_disposed)
return;
if (ptr != IntPtr.Zero)
@@ -42,7 +51,7 @@ namespace Godot
ptr = IntPtr.Zero;
}
- disposed = true;
+ _disposed = true;
}
internal RID(IntPtr ptr)
@@ -50,6 +59,9 @@ namespace Godot
this.ptr = ptr;
}
+ /// <summary>
+ /// The pointer to the native instance of this <see cref="RID"/>.
+ /// </summary>
public IntPtr NativeInstance
{
get { return ptr; }
@@ -60,25 +72,36 @@ namespace Godot
this.ptr = IntPtr.Zero;
}
+ /// <summary>
+ /// Constructs a new <see cref="RID"/> for the given <see cref="Object"/> <paramref name="from"/>.
+ /// </summary>
public RID(Object from)
{
this.ptr = godot_icall_RID_Ctor(Object.GetPtr(from));
}
+ /// <summary>
+ /// Returns the ID of the referenced resource.
+ /// </summary>
+ /// <returns>The ID of the referenced resource.</returns>
public int GetId()
{
- return godot_icall_RID_get_id(RID.GetPtr(this));
+ return godot_icall_RID_get_id(GetPtr(this));
}
+ /// <summary>
+ /// Converts this <see cref="RID"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this RID.</returns>
public override string ToString() => "[RID]";
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_RID_Ctor(IntPtr from);
+ internal static extern IntPtr godot_icall_RID_Ctor(IntPtr from);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_RID_Dtor(IntPtr ptr);
+ internal static extern void godot_icall_RID_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_RID_get_id(IntPtr ptr);
+ internal static extern int godot_icall_RID_get_id(IntPtr ptr);
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
index dec69c7f94..ec16920fed 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -20,7 +20,7 @@ namespace Godot
private Vector2 _size;
/// <summary>
- /// Beginning corner. Typically has values lower than End.
+ /// Beginning corner. Typically has values lower than <see cref="End"/>.
/// </summary>
/// <value>Directly uses a private field.</value>
public Vector2 Position
@@ -30,7 +30,7 @@ namespace Godot
}
/// <summary>
- /// Size from Position to End. Typically all components are positive.
+ /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
/// </summary>
/// <value>Directly uses a private field.</value>
@@ -41,10 +41,13 @@ namespace Godot
}
/// <summary>
- /// Ending corner. This is calculated as <see cref="Position"/> plus
- /// <see cref="Size"/>. Setting this value will change the size.
+ /// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>.
+ /// Setting this value will change the size.
/// </summary>
- /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
+ /// <value>
+ /// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
+ /// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
+ /// </value>
public Vector2 End
{
get { return _position + _size; }
@@ -52,7 +55,7 @@ namespace Godot
}
/// <summary>
- /// The area of this Rect2.
+ /// The area of this <see cref="Rect2"/>.
/// </summary>
/// <value>Equivalent to <see cref="GetArea()"/>.</value>
public real_t Area
@@ -61,10 +64,10 @@ namespace Godot
}
/// <summary>
- /// Returns a Rect2 with equivalent position and size, modified so that
+ /// Returns a <see cref="Rect2"/> with equivalent position and size, modified so that
/// the top-left corner is the origin and width and height are positive.
/// </summary>
- /// <returns>The modified Rect2.</returns>
+ /// <returns>The modified <see cref="Rect2"/>.</returns>
public Rect2 Abs()
{
Vector2 end = End;
@@ -73,14 +76,17 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection of this Rect2 and `b`.
- /// If the rectangles do not intersect, an empty Rect2 is returned.
+ /// Returns the intersection of this <see cref="Rect2"/> and <paramref name="b"/>.
+ /// If the rectangles do not intersect, an empty <see cref="Rect2"/> is returned.
/// </summary>
- /// <param name="b">The other Rect2.</param>
- /// <returns>The intersection of this Rect2 and `b`, or an empty Rect2 if they do not intersect.</returns>
+ /// <param name="b">The other <see cref="Rect2"/>.</param>
+ /// <returns>
+ /// The intersection of this <see cref="Rect2"/> and <paramref name="b"/>,
+ /// or an empty <see cref="Rect2"/> if they do not intersect.
+ /// </returns>
public Rect2 Intersection(Rect2 b)
{
- var newRect = b;
+ Rect2 newRect = b;
if (!Intersects(newRect))
{
@@ -100,10 +106,12 @@ namespace Godot
}
/// <summary>
- /// Returns true if this Rect2 completely encloses another one.
+ /// Returns <see langword="true"/> if this <see cref="Rect2"/> completely encloses another one.
/// </summary>
- /// <param name="b">The other Rect2 that may be enclosed.</param>
- /// <returns>A bool for whether or not this Rect2 encloses `b`.</returns>
+ /// <param name="b">The other <see cref="Rect2"/> that may be enclosed.</param>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not this <see cref="Rect2"/> encloses <paramref name="b"/>.
+ /// </returns>
public bool Encloses(Rect2 b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -112,13 +120,13 @@ namespace Godot
}
/// <summary>
- /// Returns this Rect2 expanded to include a given point.
+ /// Returns this <see cref="Rect2"/> expanded to include a given point.
/// </summary>
/// <param name="to">The point to include.</param>
- /// <returns>The expanded Rect2.</returns>
+ /// <returns>The expanded <see cref="Rect2"/>.</returns>
public Rect2 Expand(Vector2 to)
{
- var expanded = this;
+ Rect2 expanded = this;
Vector2 begin = expanded._position;
Vector2 end = expanded._position + expanded._size;
@@ -148,7 +156,7 @@ namespace Godot
}
/// <summary>
- /// Returns the area of the Rect2.
+ /// Returns the area of the <see cref="Rect2"/>.
/// </summary>
/// <returns>The area.</returns>
public real_t GetArea()
@@ -157,13 +165,26 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on all sides.
+ /// Returns the center of the <see cref="Rect2"/>, which is equal
+ /// to <see cref="Position"/> + (<see cref="Size"/> / 2).
/// </summary>
+ /// <returns>The center.</returns>
+ public Vector2 GetCenter()
+ {
+ return _position + (_size * 0.5f);
+ }
+
+ /// <summary>
+ /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
+ /// on all sides.
+ /// </summary>
+ /// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/>
+ /// <seealso cref="GrowSide(Side, real_t)"/>
/// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2.</returns>
+ /// <returns>The grown <see cref="Rect2"/>.</returns>
public Rect2 Grow(real_t by)
{
- var g = this;
+ Rect2 g = this;
g._position.x -= by;
g._position.y -= by;
@@ -174,16 +195,19 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on each side individually.
+ /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
+ /// on each side individually.
/// </summary>
+ /// <seealso cref="Grow(real_t)"/>
+ /// <seealso cref="GrowSide(Side, real_t)"/>
/// <param name="left">The amount to grow by on the left side.</param>
/// <param name="top">The amount to grow by on the top side.</param>
/// <param name="right">The amount to grow by on the right side.</param>
/// <param name="bottom">The amount to grow by on the bottom side.</param>
- /// <returns>The grown Rect2.</returns>
+ /// <returns>The grown <see cref="Rect2"/>.</returns>
public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
- var g = this;
+ Rect2 g = this;
g._position.x -= left;
g._position.y -= top;
@@ -194,14 +218,17 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown by the specified amount on the specified Side.
+ /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
+ /// on the specified <see cref="Side"/>.
/// </summary>
+ /// <seealso cref="Grow(real_t)"/>
+ /// <seealso cref="GrowIndividual(real_t, real_t, real_t, real_t)"/>
/// <param name="side">The side to grow.</param>
/// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2.</returns>
+ /// <returns>The grown <see cref="Rect2"/>.</returns>
public Rect2 GrowSide(Side side, real_t by)
{
- var g = this;
+ Rect2 g = this;
g = g.GrowIndividual(Side.Left == side ? by : 0,
Side.Top == side ? by : 0,
@@ -212,19 +239,25 @@ namespace Godot
}
/// <summary>
- /// Returns true if the Rect2 is flat or empty, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2"/> is flat or empty,
+ /// or <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool for whether or not the Rect2 has area.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> has area.
+ /// </returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
/// <summary>
- /// Returns true if the Rect2 contains a point, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2"/> contains a point,
+ /// or <see langword="false"/> otherwise.
/// </summary>
/// <param name="point">The point to check.</param>
- /// <returns>A bool for whether or not the Rect2 contains `point`.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="Rect2"/> contains <paramref name="point"/>.
+ /// </returns>
public bool HasPoint(Vector2 point)
{
if (point.x < _position.x)
@@ -241,15 +274,16 @@ namespace Godot
}
/// <summary>
- /// Returns true if the Rect2 overlaps with `b`
+ /// Returns <see langword="true"/> if the <see cref="Rect2"/> overlaps with <paramref name="b"/>
/// (i.e. they have at least one point in common).
///
- /// If `includeBorders` is true, they will also be considered overlapping
- /// if their borders touch, even without intersection.
+ /// If <paramref name="includeBorders"/> is <see langword="true"/>,
+ /// they will also be considered overlapping if their borders touch,
+ /// even without intersection.
/// </summary>
- /// <param name="b">The other Rect2 to check for intersections with.</param>
+ /// <param name="b">The other <see cref="Rect2"/> to check for intersections with.</param>
/// <param name="includeBorders">Whether or not to consider borders.</param>
- /// <returns>A bool for whether or not they are intersecting.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
public bool Intersects(Rect2 b, bool includeBorders = false)
{
if (includeBorders)
@@ -295,10 +329,10 @@ namespace Godot
}
/// <summary>
- /// Returns a larger Rect2 that contains this Rect2 and `b`.
+ /// Returns a larger <see cref="Rect2"/> that contains this <see cref="Rect2"/> and <paramref name="b"/>.
/// </summary>
- /// <param name="b">The other Rect2.</param>
- /// <returns>The merged Rect2.</returns>
+ /// <param name="b">The other <see cref="Rect2"/>.</param>
+ /// <returns>The merged <see cref="Rect2"/>.</returns>
public Rect2 Merge(Rect2 b)
{
Rect2 newRect;
@@ -315,7 +349,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2 from a position and size.
+ /// Constructs a <see cref="Rect2"/> from a position and size.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="size">The size.</param>
@@ -326,7 +360,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2 from a position, width, and height.
+ /// Constructs a <see cref="Rect2"/> from a position, width, and height.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="width">The width.</param>
@@ -338,7 +372,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2 from x, y, and size.
+ /// Constructs a <see cref="Rect2"/> from x, y, and size.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -350,7 +384,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2 from x, y, width, and height.
+ /// Constructs a <see cref="Rect2"/> from x, y, width, and height.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -362,16 +396,39 @@ namespace Godot
_size = new Vector2(width, height);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Rect2"/>s are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the rects are exactly equal.</returns>
public static bool operator ==(Rect2 left, Rect2 right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Rect2"/>s are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the rects are not equal.</returns>
public static bool operator !=(Rect2 left, Rect2 right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal.
+ /// </summary>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns>Whether or not the rect and the other object are exactly equal.</returns>
public override bool Equals(object obj)
{
if (obj is Rect2)
@@ -382,32 +439,49 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal.
+ /// </summary>
+ /// <param name="other">The other rect to compare.</param>
+ /// <returns>Whether or not the rects are exactly equal.</returns>
public bool Equals(Rect2 other)
{
return _position.Equals(other._position) && _size.Equals(other._size);
}
/// <summary>
- /// Returns true if this Rect2 and `other` are approximately equal, by running
- /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
+ /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
/// </summary>
- /// <param name="other">The other Rect2 to compare.</param>
- /// <returns>Whether or not the Rect2s are approximately equal.</returns>
+ /// <param name="other">The other rect to compare.</param>
+ /// <returns>Whether or not the rects are approximately equal.</returns>
public bool IsEqualApprox(Rect2 other)
{
return _position.IsEqualApprox(other._position) && _size.IsEqualApprox(other.Size);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Rect2"/>.
+ /// </summary>
+ /// <returns>A hash code for this rect.</returns>
public override int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Rect2"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this rect.</returns>
public override string ToString()
{
return $"{_position}, {_size}";
}
+ /// <summary>
+ /// Converts this <see cref="Rect2"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this rect.</returns>
public string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
index 7fb6614d2c..5d53b8330e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs
@@ -15,7 +15,7 @@ namespace Godot
private Vector2i _size;
/// <summary>
- /// Beginning corner. Typically has values lower than End.
+ /// Beginning corner. Typically has values lower than <see cref="End"/>.
/// </summary>
/// <value>Directly uses a private field.</value>
public Vector2i Position
@@ -25,7 +25,7 @@ namespace Godot
}
/// <summary>
- /// Size from Position to End. Typically all components are positive.
+ /// Size from <see cref="Position"/> to <see cref="End"/>. Typically all components are positive.
/// If the size is negative, you can use <see cref="Abs"/> to fix it.
/// </summary>
/// <value>Directly uses a private field.</value>
@@ -36,10 +36,13 @@ namespace Godot
}
/// <summary>
- /// Ending corner. This is calculated as <see cref="Position"/> plus
- /// <see cref="Size"/>. Setting this value will change the size.
+ /// Ending corner. This is calculated as <see cref="Position"/> plus <see cref="Size"/>.
+ /// Setting this value will change the size.
/// </summary>
- /// <value>Getting is equivalent to `value = Position + Size`, setting is equivalent to `Size = value - Position`.</value>
+ /// <value>
+ /// Getting is equivalent to <paramref name="value"/> = <see cref="Position"/> + <see cref="Size"/>,
+ /// setting is equivalent to <see cref="Size"/> = <paramref name="value"/> - <see cref="Position"/>
+ /// </value>
public Vector2i End
{
get { return _position + _size; }
@@ -47,7 +50,7 @@ namespace Godot
}
/// <summary>
- /// The area of this Rect2i.
+ /// The area of this <see cref="Rect2i"/>.
/// </summary>
/// <value>Equivalent to <see cref="GetArea()"/>.</value>
public int Area
@@ -56,10 +59,10 @@ namespace Godot
}
/// <summary>
- /// Returns a Rect2i with equivalent position and size, modified so that
+ /// Returns a <see cref="Rect2i"/> with equivalent position and size, modified so that
/// the top-left corner is the origin and width and height are positive.
/// </summary>
- /// <returns>The modified Rect2i.</returns>
+ /// <returns>The modified <see cref="Rect2i"/>.</returns>
public Rect2i Abs()
{
Vector2i end = End;
@@ -68,14 +71,17 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection of this Rect2i and `b`.
- /// If the rectangles do not intersect, an empty Rect2i is returned.
+ /// Returns the intersection of this <see cref="Rect2i"/> and <paramref name="b"/>.
+ /// If the rectangles do not intersect, an empty <see cref="Rect2i"/> is returned.
/// </summary>
- /// <param name="b">The other Rect2i.</param>
- /// <returns>The intersection of this Rect2i and `b`, or an empty Rect2i if they do not intersect.</returns>
+ /// <param name="b">The other <see cref="Rect2i"/>.</param>
+ /// <returns>
+ /// The intersection of this <see cref="Rect2i"/> and <paramref name="b"/>,
+ /// or an empty <see cref="Rect2i"/> if they do not intersect.
+ /// </returns>
public Rect2i Intersection(Rect2i b)
{
- var newRect = b;
+ Rect2i newRect = b;
if (!Intersects(newRect))
{
@@ -95,10 +101,12 @@ namespace Godot
}
/// <summary>
- /// Returns true if this Rect2i completely encloses another one.
+ /// Returns <see langword="true"/> if this <see cref="Rect2i"/> completely encloses another one.
/// </summary>
- /// <param name="b">The other Rect2i that may be enclosed.</param>
- /// <returns>A bool for whether or not this Rect2i encloses `b`.</returns>
+ /// <param name="b">The other <see cref="Rect2i"/> that may be enclosed.</param>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not this <see cref="Rect2i"/> encloses <paramref name="b"/>.
+ /// </returns>
public bool Encloses(Rect2i b)
{
return b._position.x >= _position.x && b._position.y >= _position.y &&
@@ -107,13 +115,13 @@ namespace Godot
}
/// <summary>
- /// Returns this Rect2i expanded to include a given point.
+ /// Returns this <see cref="Rect2i"/> expanded to include a given point.
/// </summary>
/// <param name="to">The point to include.</param>
- /// <returns>The expanded Rect2i.</returns>
+ /// <returns>The expanded <see cref="Rect2i"/>.</returns>
public Rect2i Expand(Vector2i to)
{
- var expanded = this;
+ Rect2i expanded = this;
Vector2i begin = expanded._position;
Vector2i end = expanded._position + expanded._size;
@@ -143,7 +151,7 @@ namespace Godot
}
/// <summary>
- /// Returns the area of the Rect2.
+ /// Returns the area of the <see cref="Rect2i"/>.
/// </summary>
/// <returns>The area.</returns>
public int GetArea()
@@ -152,13 +160,28 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown by the specified amount on all sides.
+ /// Returns the center of the <see cref="Rect2i"/>, which is equal
+ /// to <see cref="Position"/> + (<see cref="Size"/> / 2).
+ /// If <see cref="Size"/> is an odd number, the returned center
+ /// value will be rounded towards <see cref="Position"/>.
/// </summary>
+ /// <returns>The center.</returns>
+ public Vector2i GetCenter()
+ {
+ return _position + (_size / 2);
+ }
+
+ /// <summary>
+ /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount
+ /// on all sides.
+ /// </summary>
+ /// <seealso cref="GrowIndividual(int, int, int, int)"/>
+ /// <seealso cref="GrowSide(Side, int)"/>
/// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2i.</returns>
+ /// <returns>The grown <see cref="Rect2i"/>.</returns>
public Rect2i Grow(int by)
{
- var g = this;
+ Rect2i g = this;
g._position.x -= by;
g._position.y -= by;
@@ -169,16 +192,19 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown by the specified amount on each side individually.
+ /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount
+ /// on each side individually.
/// </summary>
+ /// <seealso cref="Grow(int)"/>
+ /// <seealso cref="GrowSide(Side, int)"/>
/// <param name="left">The amount to grow by on the left side.</param>
/// <param name="top">The amount to grow by on the top side.</param>
/// <param name="right">The amount to grow by on the right side.</param>
/// <param name="bottom">The amount to grow by on the bottom side.</param>
- /// <returns>The grown Rect2i.</returns>
+ /// <returns>The grown <see cref="Rect2i"/>.</returns>
public Rect2i GrowIndividual(int left, int top, int right, int bottom)
{
- var g = this;
+ Rect2i g = this;
g._position.x -= left;
g._position.y -= top;
@@ -189,14 +215,17 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown by the specified amount on the specified Side.
+ /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount
+ /// on the specified <see cref="Side"/>.
/// </summary>
+ /// <seealso cref="Grow(int)"/>
+ /// <seealso cref="GrowIndividual(int, int, int, int)"/>
/// <param name="side">The side to grow.</param>
/// <param name="by">The amount to grow by.</param>
- /// <returns>The grown Rect2i.</returns>
+ /// <returns>The grown <see cref="Rect2i"/>.</returns>
public Rect2i GrowSide(Side side, int by)
{
- var g = this;
+ Rect2i g = this;
g = g.GrowIndividual(Side.Left == side ? by : 0,
Side.Top == side ? by : 0,
@@ -207,19 +236,25 @@ namespace Godot
}
/// <summary>
- /// Returns true if the Rect2i is flat or empty, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2i"/> is flat or empty,
+ /// or <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool for whether or not the Rect2i has area.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> has area.
+ /// </returns>
public bool HasNoArea()
{
return _size.x <= 0 || _size.y <= 0;
}
/// <summary>
- /// Returns true if the Rect2i contains a point, or false otherwise.
+ /// Returns <see langword="true"/> if the <see cref="Rect2i"/> contains a point,
+ /// or <see langword="false"/> otherwise.
/// </summary>
/// <param name="point">The point to check.</param>
- /// <returns>A bool for whether or not the Rect2i contains `point`.</returns>
+ /// <returns>
+ /// A <see langword="bool"/> for whether or not the <see cref="Rect2i"/> contains <paramref name="point"/>.
+ /// </returns>
public bool HasPoint(Vector2i point)
{
if (point.x < _position.x)
@@ -236,15 +271,16 @@ namespace Godot
}
/// <summary>
- /// Returns true if the Rect2i overlaps with `b`
+ /// Returns <see langword="true"/> if the <see cref="Rect2i"/> overlaps with <paramref name="b"/>
/// (i.e. they have at least one point in common).
///
- /// If `includeBorders` is true, they will also be considered overlapping
- /// if their borders touch, even without intersection.
+ /// If <paramref name="includeBorders"/> is <see langword="true"/>,
+ /// they will also be considered overlapping if their borders touch,
+ /// even without intersection.
/// </summary>
- /// <param name="b">The other Rect2i to check for intersections with.</param>
+ /// <param name="b">The other <see cref="Rect2i"/> to check for intersections with.</param>
/// <param name="includeBorders">Whether or not to consider borders.</param>
- /// <returns>A bool for whether or not they are intersecting.</returns>
+ /// <returns>A <see langword="bool"/> for whether or not they are intersecting.</returns>
public bool Intersects(Rect2i b, bool includeBorders = false)
{
if (includeBorders)
@@ -274,10 +310,10 @@ namespace Godot
}
/// <summary>
- /// Returns a larger Rect2i that contains this Rect2i and `b`.
+ /// Returns a larger <see cref="Rect2i"/> that contains this <see cref="Rect2i"/> and <paramref name="b"/>.
/// </summary>
- /// <param name="b">The other Rect2i.</param>
- /// <returns>The merged Rect2i.</returns>
+ /// <param name="b">The other <see cref="Rect2i"/>.</param>
+ /// <returns>The merged <see cref="Rect2i"/>.</returns>
public Rect2i Merge(Rect2i b)
{
Rect2i newRect;
@@ -294,7 +330,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2i from a position and size.
+ /// Constructs a <see cref="Rect2i"/> from a position and size.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="size">The size.</param>
@@ -305,7 +341,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2i from a position, width, and height.
+ /// Constructs a <see cref="Rect2i"/> from a position, width, and height.
/// </summary>
/// <param name="position">The position.</param>
/// <param name="width">The width.</param>
@@ -317,7 +353,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2i from x, y, and size.
+ /// Constructs a <see cref="Rect2i"/> from x, y, and size.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -329,7 +365,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a Rect2i from x, y, width, and height.
+ /// Constructs a <see cref="Rect2i"/> from x, y, width, and height.
/// </summary>
/// <param name="x">The position's X coordinate.</param>
/// <param name="y">The position's Y coordinate.</param>
@@ -341,26 +377,53 @@ namespace Godot
_size = new Vector2i(width, height);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Rect2i"/>s are exactly equal.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the rects are equal.</returns>
public static bool operator ==(Rect2i left, Rect2i right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the
+ /// <see cref="Rect2i"/>s are not equal.
+ /// </summary>
+ /// <param name="left">The left rect.</param>
+ /// <param name="right">The right rect.</param>
+ /// <returns>Whether or not the rects are not equal.</returns>
public static bool operator !=(Rect2i left, Rect2i right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Converts this <see cref="Rect2i"/> to a <see cref="Rect2"/>.
+ /// </summary>
+ /// <param name="value">The rect to convert.</param>
public static implicit operator Rect2(Rect2i value)
{
return new Rect2(value._position, value._size);
}
+ /// <summary>
+ /// Converts a <see cref="Rect2"/> to a <see cref="Rect2i"/>.
+ /// </summary>
+ /// <param name="value">The rect to convert.</param>
public static explicit operator Rect2i(Rect2 value)
{
return new Rect2i((Vector2i)value.Position, (Vector2i)value.Size);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this rect and <paramref name="obj"/> are equal.
+ /// </summary>
+ /// <param name="obj">The other object to compare.</param>
+ /// <returns>Whether or not the rect and the other object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Rect2i)
@@ -371,21 +434,38 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if this rect and <paramref name="other"/> are equal.
+ /// </summary>
+ /// <param name="other">The other rect to compare.</param>
+ /// <returns>Whether or not the rects are equal.</returns>
public bool Equals(Rect2i other)
{
return _position.Equals(other._position) && _size.Equals(other._size);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Rect2i"/>.
+ /// </summary>
+ /// <returns>A hash code for this rect.</returns>
public override int GetHashCode()
{
return _position.GetHashCode() ^ _size.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Rect2i"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this rect.</returns>
public override string ToString()
{
return $"{_position}, {_size}";
}
+ /// <summary>
+ /// Converts this <see cref="Rect2i"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this rect.</returns>
public string ToString(string format)
{
return $"{_position.ToString(format)}, {_size.ToString(format)}";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
index 4dc630238b..2ba0493002 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalAwaiter.cs
@@ -5,9 +5,9 @@ namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
{
- private bool completed;
- private object[] result;
- private Action action;
+ private bool _completed;
+ private object[] _result;
+ private Action _action;
public SignalAwaiter(Object source, StringName signal, Object target)
{
@@ -15,24 +15,24 @@ namespace Godot
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
+ internal static extern Error godot_icall_SignalAwaiter_connect(IntPtr source, IntPtr signal, IntPtr target, SignalAwaiter awaiter);
public bool IsCompleted
{
get
{
- return completed;
+ return _completed;
}
}
public void OnCompleted(Action action)
{
- this.action = action;
+ this._action = action;
}
public object[] GetResult()
{
- return result;
+ return _result;
}
public IAwaiter<object[]> GetAwaiter()
@@ -42,13 +42,9 @@ namespace Godot
internal void SignalCallback(object[] args)
{
- completed = true;
- result = args;
-
- if (action != null)
- {
- action();
- }
+ _completed = true;
+ _result = args;
+ _action?.Invoke();
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
index dc92de7a61..5680c9d55a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/SignalInfo.cs
@@ -1,13 +1,28 @@
namespace Godot
{
+ /// <summary>
+ /// Represents a signal defined in an object.
+ /// </summary>
public struct SignalInfo
{
private readonly Object _owner;
private readonly StringName _signalName;
+ /// <summary>
+ /// Object that contains the signal.
+ /// </summary>
public Object Owner => _owner;
+ /// <summary>
+ /// Name of the signal.
+ /// </summary>
public StringName Name => _signalName;
+ /// <summary>
+ /// Creates a new <see cref="Signal"/> with the name <paramref name="name"/>
+ /// in the specified <paramref name="owner"/>.
+ /// </summary>
+ /// <param name="owner">Object that contains the signal.</param>
+ /// <param name="name">Name of the signal.</param>
public SignalInfo(Object owner, StringName name)
{
_owner = owner;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index e619ad74e9..68e6422c19 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -8,6 +8,9 @@ using System.Text.RegularExpressions;
namespace Godot
{
+ /// <summary>
+ /// Extension methods to manipulate strings.
+ /// </summary>
public static class StringExtensions
{
private static int GetSliceCount(this string instance, string splitter)
@@ -64,6 +67,11 @@ namespace Godot
/// <summary>
/// If the string is a path to a file, return the path to the file without the extension.
/// </summary>
+ /// <seealso cref="GetExtension(string)"/>
+ /// <seealso cref="GetBaseDir(string)"/>
+ /// <seealso cref="GetFile(string)"/>
+ /// <param name="instance">The path to a file.</param>
+ /// <returns>The path to the file without the extension.</returns>
public static string GetBaseName(this string instance)
{
int index = instance.LastIndexOf('.');
@@ -75,19 +83,25 @@ namespace Godot
}
/// <summary>
- /// Return <see langword="true"/> if the strings begins with the given string.
+ /// Returns <see langword="true"/> if the strings begins
+ /// with the given string <paramref name="text"/>.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="text">The beginning string.</param>
+ /// <returns>If the string begins with the given string.</returns>
public static bool BeginsWith(this string instance, string text)
{
return instance.StartsWith(text);
}
/// <summary>
- /// Return the bigrams (pairs of consecutive letters) of this string.
+ /// Returns the bigrams (pairs of consecutive letters) of this string.
/// </summary>
+ /// <param name="instance">The string that will be used.</param>
+ /// <returns>The bigrams of this string.</returns>
public static string[] Bigrams(this string instance)
{
- var b = new string[instance.Length - 1];
+ string[] b = new string[instance.Length - 1];
for (int i = 0; i < b.Length; i++)
{
@@ -99,8 +113,8 @@ namespace Godot
/// <summary>
/// Converts a string containing a binary number into an integer.
- /// Binary strings can either be prefixed with `0b` or not,
- /// and they can also start with a `-` before the optional prefix.
+ /// Binary strings can either be prefixed with <c>0b</c> or not,
+ /// and they can also start with a <c>-</c> before the optional prefix.
/// </summary>
/// <param name="instance">The string to convert.</param>
/// <returns>The converted string.</returns>
@@ -128,8 +142,14 @@ namespace Godot
}
/// <summary>
- /// Return the amount of substrings in string.
+ /// Returns the amount of substrings <paramref name="what"/> in the string.
/// </summary>
+ /// <param name="instance">The string where the substring will be searched.</param>
+ /// <param name="what">The substring that will be counted.</param>
+ /// <param name="caseSensitive">If the search is case sensitive.</param>
+ /// <param name="from">Index to start searching from.</param>
+ /// <param name="to">Index to stop searching at.</param>
+ /// <returns>Amount of substrings in the string.</returns>
public static int Count(this string instance, string what, bool caseSensitive = true, int from = 0, int to = 0)
{
if (what.Length == 0)
@@ -188,8 +208,10 @@ namespace Godot
}
/// <summary>
- /// Return a copy of the string with special characters escaped using the C language standard.
+ /// Returns a copy of the string with special characters escaped using the C language standard.
/// </summary>
+ /// <param name="instance">The string to escape.</param>
+ /// <returns>The escaped string.</returns>
public static string CEscape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
@@ -210,9 +232,11 @@ namespace Godot
}
/// <summary>
- /// Return a copy of the string with escaped characters replaced by their meanings
+ /// Returns a copy of the string with escaped characters replaced by their meanings
/// according to the C language standard.
/// </summary>
+ /// <param name="instance">The string to unescape.</param>
+ /// <returns>The unescaped string.</returns>
public static string CUnescape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
@@ -233,15 +257,17 @@ namespace Godot
}
/// <summary>
- /// Change the case of some letters. Replace underscores with spaces, convert all letters
+ /// Changes the case of some letters. Replace underscores with spaces, convert all letters
/// to lowercase then capitalize first and every letter following the space character.
/// For <c>capitalize camelCase mixed_with_underscores</c> it will return
/// <c>Capitalize Camelcase Mixed With Underscores</c>.
/// </summary>
+ /// <param name="instance">The string to capitalize.</param>
+ /// <returns>The capitalized string.</returns>
public static string Capitalize(this string instance)
{
string aux = instance.Replace("_", " ").ToLower();
- var cap = string.Empty;
+ string cap = string.Empty;
for (int i = 0; i < aux.GetSliceCount(" "); i++)
{
@@ -259,16 +285,27 @@ namespace Godot
}
/// <summary>
- /// Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
+ /// Performs a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
+ /// <seealso cref="NocasecmpTo(string, string)"/>
+ /// <seealso cref="CompareTo(string, string, bool)"/>
+ /// <param name="instance">The string to compare.</param>
+ /// <param name="to">The other string to compare.</param>
+ /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
public static int CasecmpTo(this string instance, string to)
{
return instance.CompareTo(to, caseSensitive: true);
}
/// <summary>
- /// Perform a comparison to another string, return -1 if less, 0 if equal and +1 if greater.
+ /// Performs a comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
+ /// <param name="instance">The string to compare.</param>
+ /// <param name="to">The other string to compare.</param>
+ /// <param name="caseSensitive">
+ /// If <see langword="true"/>, the comparison will be case sensitive.
+ /// </param>
+ /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
public static int CompareTo(this string instance, string to, bool caseSensitive = true)
{
if (string.IsNullOrEmpty(instance))
@@ -321,8 +358,12 @@ namespace Godot
}
/// <summary>
- /// Return <see langword="true"/> if the strings ends with the given string.
+ /// Returns <see langword="true"/> if the strings ends
+ /// with the given string <paramref name="text"/>.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="text">The ending string.</param>
+ /// <returns>If the string ends with the given string.</returns>
public static bool EndsWith(this string instance, string text)
{
return instance.EndsWith(text);
@@ -331,14 +372,36 @@ namespace Godot
/// <summary>
/// Erase <paramref name="chars"/> characters from the string starting from <paramref name="pos"/>.
/// </summary>
+ /// <param name="instance">The string to modify.</param>
+ /// <param name="pos">Starting position from which to erase.</param>
+ /// <param name="chars">Amount of characters to erase.</param>
public static void Erase(this StringBuilder instance, int pos, int chars)
{
instance.Remove(pos, chars);
}
/// <summary>
- /// If the string is a path to a file, return the extension.
- /// </summary>
+ /// Returns the extension without the leading period character (<c>.</c>)
+ /// if the string is a valid file name or path. If the string does not contain
+ /// an extension, returns an empty string instead.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print("/path/to/file.txt".GetExtension()) // "txt"
+ /// GD.Print("file.txt".GetExtension()) // "txt"
+ /// GD.Print("file.sample.txt".GetExtension()) // "txt"
+ /// GD.Print(".txt".GetExtension()) // "txt"
+ /// GD.Print("file.txt.".GetExtension()) // "" (empty string)
+ /// GD.Print("file.txt..".GetExtension()) // "" (empty string)
+ /// GD.Print("txt".GetExtension()) // "" (empty string)
+ /// GD.Print("".GetExtension()) // "" (empty string)
+ /// </code>
+ /// </example>
+ /// <seealso cref="GetBaseName(string)"/>
+ /// <seealso cref="GetBaseDir(string)"/>
+ /// <seealso cref="GetFile(string)"/>
+ /// <param name="instance">The path to a file.</param>
+ /// <returns>The extension of the file or an empty string.</returns>
public static string GetExtension(this string instance)
{
int pos = instance.FindLast(".");
@@ -352,6 +415,14 @@ namespace Godot
/// <summary>
/// Find the first occurrence of a substring. Optionally, the search starting position can be passed.
/// </summary>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindLast(string, string, bool)"/>
+ /// <seealso cref="FindLast(string, string, int, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to find.</param>
+ /// <param name="from">The search starting position.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int Find(this string instance, string what, int from = 0, bool caseSensitive = true)
{
@@ -361,6 +432,14 @@ namespace Godot
/// <summary>
/// Find the first occurrence of a char. Optionally, the search starting position can be passed.
/// </summary>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="FindLast(string, string, bool)"/>
+ /// <seealso cref="FindLast(string, string, int, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to find.</param>
+ /// <param name="from">The search starting position.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
/// <returns>The first instance of the char, or -1 if not found.</returns>
public static int Find(this string instance, char what, int from = 0, bool caseSensitive = true)
{
@@ -370,6 +449,13 @@ namespace Godot
}
/// <summary>Find the last occurrence of a substring.</summary>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindLast(string, string, int, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to find.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindLast(this string instance, string what, bool caseSensitive = true)
{
@@ -377,6 +463,14 @@ namespace Godot
}
/// <summary>Find the last occurrence of a substring specifying the search starting position.</summary>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindLast(string, string, bool)"/>
+ /// <seealso cref="FindN(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to find.</param>
+ /// <param name="from">The search starting position.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the search is case sensitive.</param>
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindLast(this string instance, string what, int from, bool caseSensitive = true)
{
@@ -387,6 +481,13 @@ namespace Godot
/// Find the first occurrence of a substring but search as case-insensitive.
/// Optionally, the search starting position can be passed.
/// </summary>
+ /// <seealso cref="Find(string, string, int, bool)"/>
+ /// <seealso cref="Find(string, char, int, bool)"/>
+ /// <seealso cref="FindLast(string, string, bool)"/>
+ /// <seealso cref="FindLast(string, string, int, bool)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to find.</param>
+ /// <param name="from">The search starting position.</param>
/// <returns>The starting position of the substring, or -1 if not found.</returns>
public static int FindN(this string instance, string what, int from = 0)
{
@@ -396,25 +497,30 @@ namespace Godot
/// <summary>
/// If the string is a path to a file, return the base directory.
/// </summary>
+ /// <seealso cref="GetBaseName(string)"/>
+ /// <seealso cref="GetExtension(string)"/>
+ /// <seealso cref="GetFile(string)"/>
+ /// <param name="instance">The path to a file.</param>
+ /// <returns>The base directory.</returns>
public static string GetBaseDir(this string instance)
{
int basepos = instance.Find("://");
string rs;
- var @base = string.Empty;
+ string directory = string.Empty;
if (basepos != -1)
{
- var end = basepos + 3;
+ int end = basepos + 3;
rs = instance.Substring(end);
- @base = instance.Substring(0, end);
+ directory = instance.Substring(0, end);
}
else
{
if (instance.BeginsWith("/"))
{
rs = instance.Substring(1);
- @base = "/";
+ directory = "/";
}
else
{
@@ -425,14 +531,19 @@ namespace Godot
int sep = Mathf.Max(rs.FindLast("/"), rs.FindLast("\\"));
if (sep == -1)
- return @base;
+ return directory;
- return @base + rs.Substr(0, sep);
+ return directory + rs.Substr(0, sep);
}
/// <summary>
/// If the string is a path to a file, return the file and ignore the base directory.
/// </summary>
+ /// <seealso cref="GetBaseName(string)"/>
+ /// <seealso cref="GetExtension(string)"/>
+ /// <seealso cref="GetBaseDir(string)"/>
+ /// <param name="instance">The path to a file.</param>
+ /// <returns>The file name.</returns>
public static string GetFile(this string instance)
{
int sep = Mathf.Max(instance.FindLast("/"), instance.FindLast("\\"));
@@ -475,6 +586,8 @@ namespace Godot
/// <summary>
/// Hash the string and return a 32 bits unsigned integer.
/// </summary>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The calculated hash of the string.</returns>
public static uint Hash(this string instance)
{
uint hash = 5381;
@@ -494,7 +607,7 @@ namespace Godot
/// <returns>The hexadecimal representation of this byte.</returns>
internal static string HexEncode(this byte b)
{
- var ret = string.Empty;
+ string ret = string.Empty;
for (int i = 0; i < 2; i++)
{
@@ -524,7 +637,7 @@ namespace Godot
/// <returns>The hexadecimal representation of this byte array.</returns>
public static string HexEncode(this byte[] bytes)
{
- var ret = string.Empty;
+ string ret = string.Empty;
foreach (byte b in bytes)
{
@@ -536,8 +649,8 @@ namespace Godot
/// <summary>
/// Converts a string containing a hexadecimal number into an integer.
- /// Hexadecimal strings can either be prefixed with `0x` or not,
- /// and they can also start with a `-` before the optional prefix.
+ /// Hexadecimal strings can either be prefixed with <c>0x</c> or not,
+ /// and they can also start with a <c>-</c> before the optional prefix.
/// </summary>
/// <param name="instance">The string to convert.</param>
/// <returns>The converted string.</returns>
@@ -565,16 +678,28 @@ namespace Godot
}
/// <summary>
- /// Insert a substring at a given position.
+ /// Inserts a substring at a given position.
/// </summary>
+ /// <param name="instance">The string to modify.</param>
+ /// <param name="pos">Position at which to insert the substring.</param>
+ /// <param name="what">Substring to insert.</param>
+ /// <returns>
+ /// The string with <paramref name="what"/> inserted at the given
+ /// position <paramref name="pos"/>.
+ /// </returns>
public static string Insert(this string instance, int pos, string what)
{
return instance.Insert(pos, what);
}
/// <summary>
- /// If the string is a path to a file or directory, return <see langword="true"/> if the path is absolute.
+ /// Returns <see langword="true"/> if the string is a path to a file or
+ /// directory and its startign point is explicitly defined. This includes
+ /// <c>res://</c>, <c>user://</c>, <c>C:\</c>, <c>/</c>, etc.
/// </summary>
+ /// <seealso cref="IsRelativePath(string)"/>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string is an absolute path.</returns>
public static bool IsAbsolutePath(this string instance)
{
if (string.IsNullOrEmpty(instance))
@@ -586,9 +711,15 @@ namespace Godot
}
/// <summary>
- /// If the string is a path to a file or directory, return <see langword="true"/> if the path is relative.
+ /// Returns <see langword="true"/> if the string is a path to a file or
+ /// directory and its starting point is implicitly defined within the
+ /// context it is being used. The starting point may refer to the current
+ /// directory (<c>./</c>), or the current <see cref="Node"/>.
/// </summary>
- public static bool IsRelPath(this string instance)
+ /// <seealso cref="IsAbsolutePath(string)"/>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string is a relative path.</returns>
+ public static bool IsRelativePath(this string instance)
{
return !IsAbsolutePath(instance);
}
@@ -596,6 +727,11 @@ namespace Godot
/// <summary>
/// Check whether this string is a subsequence of the given string.
/// </summary>
+ /// <seealso cref="IsSubsequenceOfI(string, string)"/>
+ /// <param name="instance">The subsequence to search.</param>
+ /// <param name="text">The string that contains the subsequence.</param>
+ /// <param name="caseSensitive">If <see langword="true"/>, the check is case sensitive.</param>
+ /// <returns>If the string is a subsequence of the given string.</returns>
public static bool IsSubsequenceOf(this string instance, string text, bool caseSensitive = true)
{
int len = instance.Length;
@@ -639,6 +775,10 @@ namespace Godot
/// <summary>
/// Check whether this string is a subsequence of the given string, ignoring case differences.
/// </summary>
+ /// <seealso cref="IsSubsequenceOf(string, string, bool)"/>
+ /// <param name="instance">The subsequence to search.</param>
+ /// <param name="text">The string that contains the subsequence.</param>
+ /// <returns>If the string is a subsequence of the given string.</returns>
public static bool IsSubsequenceOfI(this string instance, string text)
{
return instance.IsSubsequenceOf(text, caseSensitive: false);
@@ -647,6 +787,8 @@ namespace Godot
/// <summary>
/// Check whether the string contains a valid <see langword="float"/>.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid floating point number.</returns>
public static bool IsValidFloat(this string instance)
{
float f;
@@ -656,6 +798,8 @@ namespace Godot
/// <summary>
/// Check whether the string contains a valid color in HTML notation.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid HTML color.</returns>
public static bool IsValidHtmlColor(this string instance)
{
return Color.HtmlIsValid(instance);
@@ -666,6 +810,8 @@ namespace Godot
/// programming languages, a valid identifier may contain only letters,
/// digits and underscores (_) and the first character may not be a digit.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid identifier.</returns>
public static bool IsValidIdentifier(this string instance)
{
int len = instance.Length;
@@ -673,18 +819,15 @@ namespace Godot
if (len == 0)
return false;
+ if (instance[0] >= '0' && instance[0] <= '9')
+ return false; // Identifiers cannot start with numbers.
+
for (int i = 0; i < len; i++)
{
- if (i == 0)
- {
- if (instance[0] >= '0' && instance[0] <= '9')
- return false; // Don't start with number plz
- }
-
- bool validChar = instance[i] >= '0' &&
- instance[i] <= '9' || instance[i] >= 'a' &&
- instance[i] <= 'z' || instance[i] >= 'A' &&
- instance[i] <= 'Z' || instance[i] == '_';
+ bool validChar = instance[i] == '_' ||
+ (instance[i] >= 'a' && instance[i] <= 'z') ||
+ (instance[i] >= 'A' && instance[i] <= 'Z') ||
+ (instance[i] >= '0' && instance[i] <= '9');
if (!validChar)
return false;
@@ -696,6 +839,8 @@ namespace Godot
/// <summary>
/// Check whether the string contains a valid integer.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid integer.</returns>
public static bool IsValidInteger(this string instance)
{
int f;
@@ -705,6 +850,8 @@ namespace Godot
/// <summary>
/// Check whether the string contains a valid IP address.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>If the string contains a valid IP address.</returns>
public static bool IsValidIPAddress(this string instance)
{
// TODO: Support IPv6 addresses
@@ -728,8 +875,10 @@ namespace Godot
}
/// <summary>
- /// Return a copy of the string with special characters escaped using the JSON standard.
+ /// Returns a copy of the string with special characters escaped using the JSON standard.
/// </summary>
+ /// <param name="instance">The string to escape.</param>
+ /// <returns>The escaped string.</returns>
public static string JSONEscape(this string instance)
{
var sb = new StringBuilder(string.Copy(instance));
@@ -747,8 +896,12 @@ namespace Godot
}
/// <summary>
- /// Return an amount of characters from the left of the string.
+ /// Returns an amount of characters from the left of the string.
/// </summary>
+ /// <seealso cref="Right(string, int)"/>
+ /// <param name="instance">The original string.</param>
+ /// <param name="pos">The position in the string where the left side ends.</param>
+ /// <returns>The left side of the string from the given position.</returns>
public static string Left(this string instance, int pos)
{
if (pos <= 0)
@@ -761,8 +914,10 @@ namespace Godot
}
/// <summary>
- /// Return the length of the string in characters.
+ /// Returns the length of the string in characters.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <returns>The length of the string.</returns>
public static int Length(this string instance)
{
return instance.Length;
@@ -771,6 +926,7 @@ namespace Godot
/// <summary>
/// Returns a copy of the string with characters removed from the left.
/// </summary>
+ /// <seealso cref="RStrip(string, string)"/>
/// <param name="instance">The string to remove characters from.</param>
/// <param name="chars">The characters to be removed.</param>
/// <returns>A copy of the string with characters removed from the left.</returns>
@@ -799,6 +955,12 @@ namespace Godot
/// Do a simple expression match, where '*' matches zero or more
/// arbitrary characters and '?' matches any single character except '.'.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="expr">Expression to check.</param>
+ /// <param name="caseSensitive">
+ /// If <see langword="true"/>, the check will be case sensitive.
+ /// </param>
+ /// <returns>If the expression has any matches.</returns>
private static bool ExprMatch(this string instance, string expr, bool caseSensitive)
{
// case '\0':
@@ -824,6 +986,13 @@ namespace Godot
/// Do a simple case sensitive expression match, using ? and * wildcards
/// (see <see cref="ExprMatch(string, string, bool)"/>).
/// </summary>
+ /// <seealso cref="MatchN(string, string)"/>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="expr">Expression to check.</param>
+ /// <param name="caseSensitive">
+ /// If <see langword="true"/>, the check will be case sensitive.
+ /// </param>
+ /// <returns>If the expression has any matches.</returns>
public static bool Match(this string instance, string expr, bool caseSensitive = true)
{
if (instance.Length == 0 || expr.Length == 0)
@@ -836,6 +1005,10 @@ namespace Godot
/// Do a simple case insensitive expression match, using ? and * wildcards
/// (see <see cref="ExprMatch(string, string, bool)"/>).
/// </summary>
+ /// <seealso cref="Match(string, string, bool)"/>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="expr">Expression to check.</param>
+ /// <returns>If the expression has any matches.</returns>
public static bool MatchN(this string instance, string expr)
{
if (instance.Length == 0 || expr.Length == 0)
@@ -845,46 +1018,65 @@ namespace Godot
}
/// <summary>
- /// Return the MD5 hash of the string as an array of bytes.
+ /// Returns the MD5 hash of the string as an array of bytes.
/// </summary>
+ /// <seealso cref="MD5Text(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The MD5 hash of the string.</returns>
public static byte[] MD5Buffer(this string instance)
{
return godot_icall_String_md5_buffer(instance);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static byte[] godot_icall_String_md5_buffer(string str);
+ internal static extern byte[] godot_icall_String_md5_buffer(string str);
/// <summary>
- /// Return the MD5 hash of the string as a string.
+ /// Returns the MD5 hash of the string as a string.
/// </summary>
+ /// <seealso cref="MD5Buffer(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The MD5 hash of the string.</returns>
public static string MD5Text(this string instance)
{
return godot_icall_String_md5_text(instance);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_String_md5_text(string str);
+ internal static extern string godot_icall_String_md5_text(string str);
/// <summary>
/// Perform a case-insensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
/// </summary>
+ /// <seealso cref="CasecmpTo(string, string)"/>
+ /// <seealso cref="CompareTo(string, string, bool)"/>
+ /// <param name="instance">The string to compare.</param>
+ /// <param name="to">The other string to compare.</param>
+ /// <returns>-1 if less, 0 if equal and +1 if greater.</returns>
public static int NocasecmpTo(this string instance, string to)
{
return instance.CompareTo(to, caseSensitive: false);
}
/// <summary>
- /// Return the character code at position <paramref name="at"/>.
+ /// Returns the character code at position <paramref name="at"/>.
/// </summary>
+ /// <param name="instance">The string to check.</param>
+ /// <param name="at">The position int the string for the character to check.</param>
+ /// <returns>The character code.</returns>
public static int OrdAt(this string instance, int at)
{
return instance[at];
}
/// <summary>
- /// Format a number to have an exact number of <paramref name="digits"/> after the decimal point.
+ /// Format a number to have an exact number of <paramref name="digits"/>
+ /// after the decimal point.
/// </summary>
+ /// <seealso cref="PadZeros(string, int)"/>
+ /// <param name="instance">The string to pad.</param>
+ /// <param name="digits">Amount of digits after the decimal point.</param>
+ /// <returns>The string padded with zeroes.</returns>
public static string PadDecimals(this string instance, int digits)
{
int c = instance.Find(".");
@@ -919,8 +1111,13 @@ namespace Godot
}
/// <summary>
- /// Format a number to have an exact number of <paramref name="digits"/> before the decimal point.
+ /// Format a number to have an exact number of <paramref name="digits"/>
+ /// before the decimal point.
/// </summary>
+ /// <seealso cref="PadDecimals(string, int)"/>
+ /// <param name="instance">The string to pad.</param>
+ /// <param name="digits">Amount of digits before the decimal point.</param>
+ /// <returns>The string padded with zeroes.</returns>
public static string PadZeros(this string instance, int digits)
{
string s = instance;
@@ -952,9 +1149,13 @@ namespace Godot
}
/// <summary>
- /// If the string is a path, this concatenates <paramref name="file"/> at the end of the string as a subpath.
+ /// If the string is a path, this concatenates <paramref name="file"/>
+ /// at the end of the string as a subpath.
/// E.g. <c>"this/is".PlusFile("path") == "this/is/path"</c>.
/// </summary>
+ /// <param name="instance">The path that will be concatenated.</param>
+ /// <param name="file">File name to concatenate with the path.</param>
+ /// <returns>The concatenated path with the given file name.</returns>
public static string PlusFile(this string instance, string file)
{
if (instance.Length > 0 && instance[instance.Length - 1] == '/')
@@ -965,6 +1166,11 @@ namespace Godot
/// <summary>
/// Replace occurrences of a substring for different ones inside the string.
/// </summary>
+ /// <seealso cref="ReplaceN(string, string, string)"/>
+ /// <param name="instance">The string to modify.</param>
+ /// <param name="what">The substring to be replaced in the string.</param>
+ /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param>
+ /// <returns>The string with the substring occurrences replaced.</returns>
public static string Replace(this string instance, string what, string forwhat)
{
return instance.Replace(what, forwhat);
@@ -973,6 +1179,11 @@ namespace Godot
/// <summary>
/// Replace occurrences of a substring for different ones inside the string, but search case-insensitive.
/// </summary>
+ /// <seealso cref="Replace(string, string, string)"/>
+ /// <param name="instance">The string to modify.</param>
+ /// <param name="what">The substring to be replaced in the string.</param>
+ /// <param name="forwhat">The substring that replaces <paramref name="what"/>.</param>
+ /// <returns>The string with the substring occurrences replaced.</returns>
public static string ReplaceN(this string instance, string what, string forwhat)
{
return Regex.Replace(instance, what, forwhat, RegexOptions.IgnoreCase);
@@ -981,29 +1192,43 @@ namespace Godot
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// </summary>
+ /// <seealso cref="RFindN(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to search in the string.</param>
+ /// <param name="from">The position at which to start searching.</param>
+ /// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFind(this string instance, string what, int from = -1)
{
return godot_icall_String_rfind(instance, what, from);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_String_rfind(string str, string what, int from);
+ internal static extern int godot_icall_String_rfind(string str, string what, int from);
/// <summary>
/// Perform a search for a substring, but start from the end of the string instead of the beginning.
/// Also search case-insensitive.
/// </summary>
+ /// <seealso cref="RFind(string, string, int)"/>
+ /// <param name="instance">The string that will be searched.</param>
+ /// <param name="what">The substring to search in the string.</param>
+ /// <param name="from">The position at which to start searching.</param>
+ /// <returns>The position at which the substring was found, or -1 if not found.</returns>
public static int RFindN(this string instance, string what, int from = -1)
{
return godot_icall_String_rfindn(instance, what, from);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_String_rfindn(string str, string what, int from);
+ internal static extern int godot_icall_String_rfindn(string str, string what, int from);
/// <summary>
- /// Return the right side of the string from a given position.
+ /// Returns the right side of the string from a given position.
/// </summary>
+ /// <seealso cref="Left(string, int)"/>
+ /// <param name="instance">The original string.</param>
+ /// <param name="pos">The position in the string from which the right side starts.</param>
+ /// <returns>The right side of the string from the given position.</returns>
public static string Right(this string instance, int pos)
{
if (pos >= instance.Length)
@@ -1018,6 +1243,7 @@ namespace Godot
/// <summary>
/// Returns a copy of the string with characters removed from the right.
/// </summary>
+ /// <seealso cref="LStrip(string, string)"/>
/// <param name="instance">The string to remove characters from.</param>
/// <param name="chars">The characters to be removed.</param>
/// <returns>A copy of the string with characters removed from the right.</returns>
@@ -1042,29 +1268,41 @@ namespace Godot
return instance.Substr(0, end + 1);
}
+ /// <summary>
+ /// Returns the SHA-256 hash of the string as an array of bytes.
+ /// </summary>
+ /// <seealso cref="SHA256Text(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The SHA-256 hash of the string.</returns>
public static byte[] SHA256Buffer(this string instance)
{
return godot_icall_String_sha256_buffer(instance);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static byte[] godot_icall_String_sha256_buffer(string str);
+ internal static extern byte[] godot_icall_String_sha256_buffer(string str);
/// <summary>
- /// Return the SHA-256 hash of the string as a string.
+ /// Returns the SHA-256 hash of the string as a string.
/// </summary>
+ /// <seealso cref="SHA256Buffer(string)"/>
+ /// <param name="instance">The string to hash.</param>
+ /// <returns>The SHA-256 hash of the string.</returns>
public static string SHA256Text(this string instance)
{
return godot_icall_String_sha256_text(instance);
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_String_sha256_text(string str);
+ internal static extern string godot_icall_String_sha256_text(string str);
/// <summary>
- /// Return the similarity index of the text compared to this string.
+ /// Returns the similarity index of the text compared to this string.
/// 1 means totally similar and 0 means totally dissimilar.
/// </summary>
+ /// <param name="instance">The string to compare.</param>
+ /// <param name="text">The other string to compare.</param>
+ /// <returns>The similarity index.</returns>
public static float Similarity(this string instance, string text)
{
if (instance == text)
@@ -1111,12 +1349,19 @@ namespace Godot
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_String_simplify_path(string str);
+ internal static extern string godot_icall_String_simplify_path(string str);
/// <summary>
/// Split the string by a divisor string, return an array of the substrings.
/// Example "One,Two,Three" will return ["One","Two","Three"] if split by ",".
/// </summary>
+ /// <seealso cref="SplitFloats(string, string, bool)"/>
+ /// <param name="instance">The string to split.</param>
+ /// <param name="divisor">The divisor string that splits the string.</param>
+ /// <param name="allowEmpty">
+ /// If <see langword="true"/>, the array may include empty strings.
+ /// </param>
+ /// <returns>The array of strings split from the string.</returns>
public static string[] Split(this string instance, string divisor, bool allowEmpty = true)
{
return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
@@ -1126,6 +1371,13 @@ namespace Godot
/// Split the string in floats by using a divisor string, return an array of the substrings.
/// Example "1,2.5,3" will return [1,2.5,3] if split by ",".
/// </summary>
+ /// <seealso cref="Split(string, string, bool)"/>
+ /// <param name="instance">The string to split.</param>
+ /// <param name="divisor">The divisor string that splits the string.</param>
+ /// <param name="allowEmpty">
+ /// If <see langword="true"/>, the array may include empty floats.
+ /// </param>
+ /// <returns>The array of floats split from the string.</returns>
public static float[] SplitFloats(this string instance, string divisor, bool allowEmpty = true)
{
var ret = new List<float>();
@@ -1148,7 +1400,8 @@ namespace Godot
return ret.ToArray();
}
- private static readonly char[] _nonPrintable = {
+ private static readonly char[] _nonPrintable =
+ {
(char)00, (char)01, (char)02, (char)03, (char)04, (char)05,
(char)06, (char)07, (char)08, (char)09, (char)10, (char)11,
(char)12, (char)13, (char)14, (char)15, (char)16, (char)17,
@@ -1158,9 +1411,13 @@ namespace Godot
};
/// <summary>
- /// Return a copy of the string stripped of any non-printable character at the beginning and the end.
+ /// Returns a copy of the string stripped of any non-printable character at the beginning and the end.
/// The optional arguments are used to toggle stripping on the left and right edges respectively.
/// </summary>
+ /// <param name="instance">The string to strip.</param>
+ /// <param name="left">If the left side should be stripped.</param>
+ /// <param name="right">If the right side should be stripped.</param>
+ /// <returns>The string stripped of any non-printable characters.</returns>
public static string StripEdges(this string instance, bool left = true, bool right = true)
{
if (left)
@@ -1174,8 +1431,14 @@ namespace Godot
}
/// <summary>
- /// Return part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>.
+ /// Returns part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>.
/// </summary>
+ /// <param name="instance">The string to slice.</param>
+ /// <param name="from">The position in the string that the part starts from.</param>
+ /// <param name="len">The length of the returned part.</param>
+ /// <returns>
+ /// Part of the string from the position <paramref name="from"/>, with length <paramref name="len"/>.
+ /// </returns>
public static string Substr(this string instance, int from, int len)
{
int max = instance.Length - from;
@@ -1183,52 +1446,70 @@ namespace Godot
}
/// <summary>
- /// Convert the String (which is a character array) to PackedByteArray (which is an array of bytes).
+ /// Converts the String (which is a character array) to PackedByteArray (which is an array of bytes).
/// The conversion is speeded up in comparison to <see cref="ToUTF8(string)"/> with the assumption
/// that all the characters the String contains are only ASCII characters.
/// </summary>
+ /// <seealso cref="ToUTF8(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The string as ASCII encoded bytes.</returns>
public static byte[] ToAscii(this string instance)
{
return Encoding.ASCII.GetBytes(instance);
}
/// <summary>
- /// Convert a string, containing a decimal number, into a <see langword="float" />.
+ /// Converts a string, containing a decimal number, into a <see langword="float" />.
/// </summary>
+ /// <seealso cref="ToInt(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The number representation of the string.</returns>
public static float ToFloat(this string instance)
{
return float.Parse(instance);
}
/// <summary>
- /// Convert a string, containing an integer number, into an <see langword="int" />.
+ /// Converts a string, containing an integer number, into an <see langword="int" />.
/// </summary>
+ /// <seealso cref="ToFloat(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The number representation of the string.</returns>
public static int ToInt(this string instance)
{
return int.Parse(instance);
}
/// <summary>
- /// Return the string converted to lowercase.
+ /// Returns the string converted to lowercase.
/// </summary>
+ /// <seealso cref="ToUpper(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The string converted to lowercase.</returns>
public static string ToLower(this string instance)
{
return instance.ToLower();
}
/// <summary>
- /// Return the string converted to uppercase.
+ /// Returns the string converted to uppercase.
/// </summary>
+ /// <seealso cref="ToLower(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The string converted to uppercase.</returns>
public static string ToUpper(this string instance)
{
return instance.ToUpper();
}
/// <summary>
- /// Convert the String (which is an array of characters) to PackedByteArray (which is an array of bytes).
+ /// Converts the String (which is an array of characters) to PackedByteArray (which is an array of bytes).
/// The conversion is a bit slower than <see cref="ToAscii(string)"/>, but supports all UTF-8 characters.
/// Therefore, you should prefer this function over <see cref="ToAscii(string)"/>.
/// </summary>
+ /// <seealso cref="ToAscii(string)"/>
+ /// <param name="instance">The string to convert.</param>
+ /// <returns>The string as UTF-8 encoded bytes.</returns>
public static byte[] ToUTF8(this string instance)
{
return Encoding.UTF8.GetBytes(instance);
@@ -1237,8 +1518,8 @@ namespace Godot
/// <summary>
/// Decodes a string in URL encoded format. This is meant to
/// decode parameters in a URL when receiving an HTTP request.
- /// This mostly wraps around `System.Uri.UnescapeDataString()`,
- /// but also handles `+`.
+ /// This mostly wraps around <see cref="Uri.UnescapeDataString"/>,
+ /// but also handles <c>+</c>.
/// See <see cref="URIEncode"/> for encoding.
/// </summary>
/// <param name="instance">The string to decode.</param>
@@ -1251,7 +1532,7 @@ namespace Godot
/// <summary>
/// Encodes a string to URL friendly format. This is meant to
/// encode parameters in a URL when sending an HTTP request.
- /// This wraps around `System.Uri.EscapeDataString()`.
+ /// This wraps around <see cref="Uri.EscapeDataString"/>.
/// See <see cref="URIDecode"/> for decoding.
/// </summary>
/// <param name="instance">The string to encode.</param>
@@ -1262,17 +1543,23 @@ namespace Godot
}
/// <summary>
- /// Return a copy of the string with special characters escaped using the XML standard.
+ /// Returns a copy of the string with special characters escaped using the XML standard.
/// </summary>
+ /// <seealso cref="XMLUnescape(string)"/>
+ /// <param name="instance">The string to escape.</param>
+ /// <returns>The escaped string.</returns>
public static string XMLEscape(this string instance)
{
return SecurityElement.Escape(instance);
}
/// <summary>
- /// Return a copy of the string with escaped characters replaced by their meanings
+ /// Returns a copy of the string with escaped characters replaced by their meanings
/// according to the XML standard.
/// </summary>
+ /// <seealso cref="XMLEscape(string)"/>
+ /// <param name="instance">The string to unescape.</param>
+ /// <returns>The unescaped string.</returns>
public static string XMLUnescape(this string instance)
{
return SecurityElement.FromString(instance).Text;
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
index 7700b6d4ed..b1d504410b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs
@@ -3,6 +3,13 @@ using System.Runtime.CompilerServices;
namespace Godot
{
+ /// <summary>
+ /// StringNames are immutable strings designed for general-purpose representation of unique names.
+ /// StringName ensures that only one instance of a given name exists (so two StringNames with the
+ /// same value are the same object).
+ /// Comparing them is much faster than with regular strings, because only the pointers are compared,
+ /// not the whole strings.
+ /// </summary>
public sealed partial class StringName : IDisposable
{
private IntPtr ptr;
@@ -23,6 +30,9 @@ namespace Godot
Dispose(false);
}
+ /// <summary>
+ /// Disposes of this <see cref="StringName"/>.
+ /// </summary>
public void Dispose()
{
Dispose(true);
@@ -43,25 +53,48 @@ namespace Godot
this.ptr = ptr;
}
+ /// <summary>
+ /// Constructs an empty <see cref="StringName"/>.
+ /// </summary>
public StringName()
{
ptr = IntPtr.Zero;
}
+ /// <summary>
+ /// Constructs a <see cref="StringName"/> from the given <paramref name="path"/> string.
+ /// </summary>
+ /// <param name="path">String to construct the <see cref="StringName"/> from.</param>
public StringName(string path)
{
ptr = path == null ? IntPtr.Zero : godot_icall_StringName_Ctor(path);
}
+ /// <summary>
+ /// Converts a string to a <see cref="StringName"/>.
+ /// </summary>
+ /// <param name="from">The string to convert.</param>
public static implicit operator StringName(string from) => new StringName(from);
+ /// <summary>
+ /// Converts a <see cref="StringName"/> to a string.
+ /// </summary>
+ /// <param name="from">The <see cref="StringName"/> to convert.</param>
public static implicit operator string(StringName from) => from.ToString();
+ /// <summary>
+ /// Converts this <see cref="StringName"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this <see cref="StringName"/>.</returns>
public override string ToString()
{
return ptr == IntPtr.Zero ? string.Empty : godot_icall_StringName_operator_String(GetPtr(this));
}
+ /// <summary>
+ /// Check whether this <see cref="StringName"/> is empty.
+ /// </summary>
+ /// <returns>If the <see cref="StringName"/> is empty.</returns>
public bool IsEmpty()
{
return ptr == IntPtr.Zero || godot_icall_StringName_is_empty(GetPtr(this));
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
index 62a6fe6959..8e253388bf 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -21,18 +21,17 @@ namespace Godot
public struct Transform2D : IEquatable<Transform2D>
{
/// <summary>
- /// The basis matrix's X vector (column 0). Equivalent to array index `[0]`.
+ /// The basis matrix's X vector (column 0). Equivalent to array index <c>[0]</c>.
/// </summary>
- /// <value></value>
public Vector2 x;
/// <summary>
- /// The basis matrix's Y vector (column 1). Equivalent to array index `[1]`.
+ /// The basis matrix's Y vector (column 1). Equivalent to array index <c>[1]</c>.
/// </summary>
public Vector2 y;
/// <summary>
- /// The origin vector (column 2, the third column). Equivalent to array index `[2]`.
+ /// The origin vector (column 2, the third column). Equivalent to array index <c>[2]</c>.
/// The origin vector represents translation.
/// </summary>
public Vector2 origin;
@@ -77,7 +76,8 @@ namespace Godot
}
/// <summary>
- /// Access whole columns in the form of Vector2. The third column is the origin vector.
+ /// Access whole columns in the form of <see cref="Vector2"/>.
+ /// The third column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column vector.</param>
public Vector2 this[int column]
@@ -116,7 +116,8 @@ namespace Godot
}
/// <summary>
- /// Access matrix elements in column-major order. The third column is the origin vector.
+ /// Access matrix elements in column-major order.
+ /// The third column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column, the matrix horizontal position.</param>
/// <param name="row">Which row, the matrix vertical position.</param>
@@ -138,6 +139,7 @@ namespace Godot
/// Returns the inverse of the transform, under the assumption that
/// the transformation is composed of rotation, scaling, and translation.
/// </summary>
+ /// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
public Transform2D AffineInverse()
{
@@ -146,7 +148,7 @@ namespace Godot
if (det == 0)
throw new InvalidOperationException("Matrix determinant is zero and cannot be inverted.");
- var inv = this;
+ Transform2D inv = this;
real_t temp = inv[0, 0];
inv[0, 0] = inv[1, 1];
@@ -173,13 +175,14 @@ namespace Godot
/// <returns>The determinant of the basis matrix.</returns>
private real_t BasisDeterminant()
{
- return x.x * y.y - x.y * y.x;
+ return (x.x * y.y) - (x.y * y.x);
}
/// <summary>
/// Returns a vector transformed (multiplied) by the basis matrix.
- /// This method does not account for translation (the origin vector).
+ /// This method does not account for translation (the <see cref="origin"/> vector).
/// </summary>
+ /// <seealso cref="BasisXformInv(Vector2)"/>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
public Vector2 BasisXform(Vector2 v)
@@ -189,11 +192,12 @@ namespace Godot
/// <summary>
/// Returns a vector transformed (multiplied) by the inverse basis matrix.
- /// This method does not account for translation (the origin vector).
+ /// This method does not account for translation (the <see cref="origin"/> vector).
///
/// Note: This results in a multiplication by the inverse of the
/// basis matrix only if it represents a rotation-reflection.
/// </summary>
+ /// <seealso cref="BasisXform(Vector2)"/>
/// <param name="v">A vector to inversely transform.</param>
/// <returns>The inversely transformed vector.</returns>
public Vector2 BasisXformInv(Vector2 v)
@@ -202,7 +206,7 @@ namespace Godot
}
/// <summary>
- /// Interpolates this transform to the other `transform` by `weight`.
+ /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -233,8 +237,8 @@ namespace Godot
else
{
real_t angle = weight * Mathf.Acos(dot);
- Vector2 v3 = (v2 - v1 * dot).Normalized();
- v = v1 * Mathf.Cos(angle) + v3 * Mathf.Sin(angle);
+ Vector2 v3 = (v2 - (v1 * dot)).Normalized();
+ v = (v1 * Mathf.Cos(angle)) + (v3 * Mathf.Sin(angle));
}
// Extract parameters
@@ -258,7 +262,7 @@ namespace Godot
/// <returns>The inverse matrix.</returns>
public Transform2D Inverse()
{
- var inv = this;
+ Transform2D inv = this;
// Swap
real_t temp = inv.x.y;
@@ -277,13 +281,13 @@ namespace Godot
/// <returns>The orthonormalized transform.</returns>
public Transform2D Orthonormalized()
{
- var on = this;
+ Transform2D on = this;
Vector2 onX = on.x;
Vector2 onY = on.y;
onX.Normalize();
- onY = onY - onX * onX.Dot(onY);
+ onY = onY - (onX * onX.Dot(onY));
onY.Normalize();
on.x = onX;
@@ -293,7 +297,7 @@ namespace Godot
}
/// <summary>
- /// Rotates the transform by `phi` (in radians), using matrix multiplication.
+ /// Rotates the transform by <paramref name="phi"/> (in radians), using matrix multiplication.
/// </summary>
/// <param name="phi">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
@@ -309,7 +313,7 @@ namespace Godot
/// <returns>The scaled transformation matrix.</returns>
public Transform2D Scaled(Vector2 scale)
{
- var copy = this;
+ Transform2D copy = this;
copy.x *= scale;
copy.y *= scale;
copy.origin *= scale;
@@ -326,16 +330,16 @@ namespace Godot
private real_t Tdotx(Vector2 with)
{
- return this[0, 0] * with[0] + this[1, 0] * with[1];
+ return (this[0, 0] * with[0]) + (this[1, 0] * with[1]);
}
private real_t Tdoty(Vector2 with)
{
- return this[0, 1] * with[0] + this[1, 1] * with[1];
+ return (this[0, 1] * with[0]) + (this[1, 1] * with[1]);
}
/// <summary>
- /// Translates the transform by the given `offset`,
+ /// Translates the transform by the given <paramref name="offset"/>,
/// relative to the transform's basis vectors.
///
/// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
@@ -345,7 +349,7 @@ namespace Godot
/// <returns>The translated matrix.</returns>
public Transform2D Translated(Vector2 offset)
{
- var copy = this;
+ Transform2D copy = this;
copy.origin += copy.BasisXform(offset);
return copy;
}
@@ -353,8 +357,10 @@ namespace Godot
/// <summary>
/// Returns a vector transformed (multiplied) by this transformation matrix.
/// </summary>
+ /// <seealso cref="XformInv(Vector2)"/>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
+ [Obsolete("Xform is deprecated. Use the multiplication operator (Transform2D * Vector2) instead.")]
public Vector2 Xform(Vector2 v)
{
return new Vector2(Tdotx(v), Tdoty(v)) + origin;
@@ -363,8 +369,10 @@ namespace Godot
/// <summary>
/// Returns a vector transformed (multiplied) by the inverse transformation matrix.
/// </summary>
+ /// <seealso cref="Xform(Vector2)"/>
/// <param name="v">A vector to inversely transform.</param>
/// <returns>The inversely transformed vector.</returns>
+ [Obsolete("XformInv is deprecated. Use the multiplication operator (Vector2 * Transform2D) instead.")]
public Vector2 XformInv(Vector2 v)
{
Vector2 vInv = v - origin;
@@ -378,20 +386,20 @@ namespace Godot
/// <summary>
/// The identity transform, with no translation, rotation, or scaling applied.
- /// This is used as a replacement for `Transform2D()` in GDScript.
- /// Do not use `new Transform2D()` with no arguments in C#, because it sets all values to zero.
+ /// This is used as a replacement for <c>Transform2D()</c> in GDScript.
+ /// Do not use <c>new Transform2D()</c> with no arguments in C#, because it sets all values to zero.
/// </summary>
- /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Down, Vector2.Zero)</c>.</value>
public static Transform2D Identity { get { return _identity; } }
/// <summary>
/// The transform that will flip something along the X axis.
/// </summary>
- /// <value>Equivalent to `new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform2D(Vector2.Left, Vector2.Down, Vector2.Zero)</c>.</value>
public static Transform2D FlipX { get { return _flipX; } }
/// <summary>
/// The transform that will flip something along the Y axis.
/// </summary>
- /// <value>Equivalent to `new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform2D(Vector2.Right, Vector2.Up, Vector2.Zero)</c>.</value>
public static Transform2D FlipY { get { return _flipY; } }
/// <summary>
@@ -411,12 +419,12 @@ namespace Godot
/// Constructs a transformation matrix from the given components.
/// Arguments are named such that xy is equal to calling x.y
/// </summary>
- /// <param name="xx">The X component of the X column vector, accessed via `t.x.x` or `[0][0]`</param>
- /// <param name="xy">The Y component of the X column vector, accessed via `t.x.y` or `[0][1]`</param>
- /// <param name="yx">The X component of the Y column vector, accessed via `t.y.x` or `[1][0]`</param>
- /// <param name="yy">The Y component of the Y column vector, accessed via `t.y.y` or `[1][1]`</param>
- /// <param name="ox">The X component of the origin vector, accessed via `t.origin.x` or `[2][0]`</param>
- /// <param name="oy">The Y component of the origin vector, accessed via `t.origin.y` or `[2][1]`</param>
+ /// <param name="xx">The X component of the X column vector, accessed via <c>t.x.x</c> or <c>[0][0]</c>.</param>
+ /// <param name="xy">The Y component of the X column vector, accessed via <c>t.x.y</c> or <c>[0][1]</c>.</param>
+ /// <param name="yx">The X component of the Y column vector, accessed via <c>t.y.x</c> or <c>[1][0]</c>.</param>
+ /// <param name="yy">The Y component of the Y column vector, accessed via <c>t.y.y</c> or <c>[1][1]</c>.</param>
+ /// <param name="ox">The X component of the origin vector, accessed via <c>t.origin.x</c> or <c>[2][0]</c>.</param>
+ /// <param name="oy">The Y component of the origin vector, accessed via <c>t.origin.y</c> or <c>[2][1]</c>.</param>
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
x = new Vector2(xx, xy);
@@ -425,21 +433,30 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from a rotation value and origin vector.
+ /// Constructs a transformation matrix from a <paramref name="rotation"/> value and
+ /// <paramref name="origin"/> vector.
/// </summary>
- /// <param name="rot">The rotation of the new transform, in radians.</param>
- /// <param name="pos">The origin vector, or column index 2.</param>
- public Transform2D(real_t rot, Vector2 pos)
+ /// <param name="rotation">The rotation of the new transform, in radians.</param>
+ /// <param name="origin">The origin vector, or column index 2.</param>
+ public Transform2D(real_t rotation, Vector2 origin)
{
- x.x = y.y = Mathf.Cos(rot);
- x.y = y.x = Mathf.Sin(rot);
+ x.x = y.y = Mathf.Cos(rotation);
+ x.y = y.x = Mathf.Sin(rotation);
y.x *= -1;
- origin = pos;
+ this.origin = origin;
}
+ /// <summary>
+ /// Composes these two transformation matrices by multiplying them
+ /// together. This has the effect of transforming the second transform
+ /// (the child) by the first transform (the parent).
+ /// </summary>
+ /// <param name="left">The parent transform.</param>
+ /// <param name="right">The child transform.</param>
+ /// <returns>The composed transform.</returns>
public static Transform2D operator *(Transform2D left, Transform2D right)
{
- left.origin = left.Xform(right.origin);
+ left.origin = left * right.origin;
real_t x0 = left.Tdotx(right.x);
real_t x1 = left.Tdoty(right.x);
@@ -454,29 +471,150 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns a Vector2 transformed (multiplied) by transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="vector">A Vector2 to transform.</param>
+ /// <returns>The transformed Vector2.</returns>
+ public static Vector2 operator *(Transform2D transform, Vector2 vector)
+ {
+ return new Vector2(transform.Tdotx(vector), transform.Tdoty(vector)) + transform.origin;
+ }
+
+ /// <summary>
+ /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="vector">A Vector2 to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Vector2.</returns>
+ public static Vector2 operator *(Vector2 vector, Transform2D transform)
+ {
+ Vector2 vInv = vector - transform.origin;
+ return new Vector2(transform.x.Dot(vInv), transform.y.Dot(vInv));
+ }
+
+ /// <summary>
+ /// Returns a Rect2 transformed (multiplied) by transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="rect">A Rect2 to transform.</param>
+ /// <returns>The transformed Rect2.</returns>
+ public static Rect2 operator *(Transform2D transform, Rect2 rect)
+ {
+ Vector2 pos = transform * rect.Position;
+ Vector2 toX = transform.x * rect.Size.x;
+ Vector2 toY = transform.y * rect.Size.y;
+
+ return new Rect2(pos, rect.Size).Expand(pos + toX).Expand(pos + toY).Expand(pos + toX + toY);
+ }
+
+ /// <summary>
+ /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="rect">A Rect2 to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed Rect2.</returns>
+ public static Rect2 operator *(Rect2 rect, Transform2D transform)
+ {
+ Vector2 pos = rect.Position * transform;
+ Vector2 to1 = new Vector2(rect.Position.x, rect.Position.y + rect.Size.y) * transform;
+ Vector2 to2 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y + rect.Size.y) * transform;
+ Vector2 to3 = new Vector2(rect.Position.x + rect.Size.x, rect.Position.y) * transform;
+
+ return new Rect2(pos, rect.Size).Expand(to1).Expand(to2).Expand(to3);
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector2[] transformed (multiplied) by transformation matrix.
+ /// </summary>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <param name="array">A Vector2[] to transform.</param>
+ /// <returns>The transformed copy of the Vector2[].</returns>
+ public static Vector2[] operator *(Transform2D transform, Vector2[] array)
+ {
+ Vector2[] newArray = new Vector2[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = transform * array[i];
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
+ /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix.
+ /// </summary>
+ /// <param name="array">A Vector2[] to inversely transform.</param>
+ /// <param name="transform">The transformation to apply.</param>
+ /// <returns>The inversely transformed copy of the Vector2[].</returns>
+ public static Vector2[] operator *(Vector2[] array, Transform2D transform)
+ {
+ Vector2[] newArray = new Vector2[array.Length];
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ newArray[i] = array[i] * transform;
+ }
+
+ return newArray;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left transform.</param>
+ /// <param name="right">The right transform.</param>
+ /// <returns>Whether or not the transforms are exactly equal.</returns>
public static bool operator ==(Transform2D left, Transform2D right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left transform.</param>
+ /// <param name="right">The right transform.</param>
+ /// <returns>Whether or not the transforms are not equal.</returns>
public static bool operator !=(Transform2D left, Transform2D right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transform is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the transform and the object are exactly equal.</returns>
public override bool Equals(object obj)
{
return obj is Transform2D transform2D && Equals(transform2D);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other transform to compare.</param>
+ /// <returns>Whether or not the matrices are exactly equal.</returns>
public bool Equals(Transform2D other)
{
return x.Equals(other.x) && y.Equals(other.y) && origin.Equals(other.origin);
}
/// <summary>
- /// Returns true if this transform and `other` are approximately equal, by running
- /// <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
+ /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Vector2.IsEqualApprox(Vector2)"/> on each component.
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are approximately equal.</returns>
@@ -485,16 +623,28 @@ namespace Godot
return x.IsEqualApprox(other.x) && y.IsEqualApprox(other.y) && origin.IsEqualApprox(other.origin);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Transform2D"/>.
+ /// </summary>
+ /// <returns>A hash code for this transform.</returns>
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() ^ origin.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Transform2D"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this transform.</returns>
public override string ToString()
{
return $"[X: {x}, Y: {y}, O: {origin}]";
}
+ /// <summary>
+ /// Converts this <see cref="Transform2D"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this transform.</returns>
public string ToString(string format)
{
return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, O: {origin.ToString(format)}]";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index afc6a65a45..5d9aabdd2f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -28,12 +28,13 @@ namespace Godot
public Basis basis;
/// <summary>
- /// The origin vector (column 3, the fourth column). Equivalent to array index `[3]`.
+ /// The origin vector (column 3, the fourth column). Equivalent to array index <c>[3]</c>.
/// </summary>
public Vector3 origin;
/// <summary>
- /// Access whole columns in the form of Vector3. The fourth column is the origin vector.
+ /// Access whole columns in the form of <see cref="Vector3"/>.
+ /// The fourth column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column vector.</param>
public Vector3 this[int column]
@@ -77,7 +78,8 @@ namespace Godot
}
/// <summary>
- /// Access matrix elements in column-major order. The fourth column is the origin vector.
+ /// Access matrix elements in column-major order.
+ /// The fourth column is the <see cref="origin"/> vector.
/// </summary>
/// <param name="column">Which column, the matrix horizontal position.</param>
/// <param name="row">Which row, the matrix vertical position.</param>
@@ -106,6 +108,7 @@ namespace Godot
/// Returns the inverse of the transform, under the assumption that
/// the transformation is composed of rotation, scaling, and translation.
/// </summary>
+ /// <seealso cref="Inverse"/>
/// <returns>The inverse transformation matrix.</returns>
public Transform3D AffineInverse()
{
@@ -114,7 +117,7 @@ namespace Godot
}
/// <summary>
- /// Interpolates this transform to the other `transform` by `weight`.
+ /// Interpolates this transform to the other <paramref name="transform"/> by <paramref name="weight"/>.
/// </summary>
/// <param name="transform">The other transform.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -132,7 +135,9 @@ namespace Godot
Vector3 destinationLocation = transform.origin;
var interpolated = new Transform3D();
- interpolated.basis.SetQuaternionScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.Lerp(destinationScale, weight));
+ Quaternion quaternion = sourceRotation.Slerp(destinationRotation, weight).Normalized();
+ Vector3 scale = sourceScale.Lerp(destinationScale, weight);
+ interpolated.basis.SetQuaternionScale(quaternion, scale);
interpolated.origin = sourceLocation.Lerp(destinationLocation, weight);
return interpolated;
@@ -152,20 +157,20 @@ namespace Godot
/// <summary>
/// Returns a copy of the transform rotated such that its
- /// -Z axis (forward) points towards the target position.
+ /// -Z axis (forward) points towards the <paramref name="target"/> position.
///
- /// The transform will first be rotated around the given up vector,
- /// and then fully aligned to the target by a further rotation around
- /// an axis perpendicular to both the target and up vectors.
+ /// The transform will first be rotated around the given <paramref name="up"/> vector,
+ /// and then fully aligned to the <paramref name="target"/> by a further rotation around
+ /// an axis perpendicular to both the <paramref name="target"/> and <paramref name="up"/> vectors.
///
/// Operations take place in global space.
/// </summary>
/// <param name="target">The object to look at.</param>
- /// <param name="up">The relative up direction</param>
+ /// <param name="up">The relative up direction.</param>
/// <returns>The resulting transform.</returns>
public Transform3D LookingAt(Vector3 target, Vector3 up)
{
- var t = this;
+ Transform3D t = this;
t.SetLookAt(origin, target, up);
return t;
}
@@ -181,7 +186,7 @@ namespace Godot
}
/// <summary>
- /// Rotates the transform around the given `axis` by `phi` (in radians),
+ /// Rotates the transform around the given <paramref name="axis"/> by <paramref name="phi"/> (in radians),
/// using matrix multiplication. The axis must be a normalized vector.
/// </summary>
/// <param name="axis">The axis to rotate around. Must be normalized.</param>
@@ -226,7 +231,7 @@ namespace Godot
}
/// <summary>
- /// Translates the transform by the given `offset`,
+ /// Translates the transform by the given <paramref name="offset"/>,
/// relative to the transform's basis vectors.
///
/// Unlike <see cref="Rotated"/> and <see cref="Scaled"/>,
@@ -247,6 +252,7 @@ namespace Godot
/// <summary>
/// Returns a vector transformed (multiplied) by this transformation matrix.
/// </summary>
+ /// <seealso cref="XformInv(Vector3)"/>
/// <param name="v">A vector to transform.</param>
/// <returns>The transformed vector.</returns>
public Vector3 Xform(Vector3 v)
@@ -265,6 +271,7 @@ namespace Godot
/// Note: This results in a multiplication by the inverse of the
/// transformation matrix only if it represents a rotation-reflection.
/// </summary>
+ /// <seealso cref="Xform(Vector3)"/>
/// <param name="v">A vector to inversely transform.</param>
/// <returns>The inversely transformed vector.</returns>
public Vector3 XformInv(Vector3 v)
@@ -273,9 +280,9 @@ namespace Godot
return new Vector3
(
- basis.Row0[0] * vInv.x + basis.Row1[0] * vInv.y + basis.Row2[0] * vInv.z,
- basis.Row0[1] * vInv.x + basis.Row1[1] * vInv.y + basis.Row2[1] * vInv.z,
- basis.Row0[2] * vInv.x + basis.Row1[2] * vInv.y + basis.Row2[2] * vInv.z
+ (basis.Row0[0] * vInv.x) + (basis.Row1[0] * vInv.y) + (basis.Row2[0] * vInv.z),
+ (basis.Row0[1] * vInv.x) + (basis.Row1[1] * vInv.y) + (basis.Row2[1] * vInv.z),
+ (basis.Row0[2] * vInv.x) + (basis.Row1[2] * vInv.y) + (basis.Row2[2] * vInv.z)
);
}
@@ -287,25 +294,25 @@ namespace Godot
/// <summary>
/// The identity transform, with no translation, rotation, or scaling applied.
- /// This is used as a replacement for `Transform()` in GDScript.
- /// Do not use `new Transform()` with no arguments in C#, because it sets all values to zero.
+ /// This is used as a replacement for <c>Transform()</c> in GDScript.
+ /// Do not use <c>new Transform()</c> with no arguments in C#, because it sets all values to zero.
/// </summary>
- /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value>
public static Transform3D Identity { get { return _identity; } }
/// <summary>
/// The transform that will flip something along the X axis.
/// </summary>
- /// <value>Equivalent to `new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform(Vector3.Left, Vector3.Up, Vector3.Back, Vector3.Zero)</c>.</value>
public static Transform3D FlipX { get { return _flipX; } }
/// <summary>
/// The transform that will flip something along the Y axis.
/// </summary>
- /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Down, Vector3.Back, Vector3.Zero)</c>.</value>
public static Transform3D FlipY { get { return _flipY; } }
/// <summary>
/// The transform that will flip something along the Z axis.
/// </summary>
- /// <value>Equivalent to `new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)`.</value>
+ /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)</c>.</value>
public static Transform3D FlipZ { get { return _flipZ; } }
/// <summary>
@@ -322,9 +329,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from the given quaternion and origin vector.
+ /// Constructs a transformation matrix from the given <paramref name="quaternion"/>
+ /// and <paramref name="origin"/> vector.
/// </summary>
- /// <param name="quaternion">The <see cref="Godot.Quaternion"/> to create the basis from.</param>
+ /// <param name="quaternion">The <see cref="Quaternion"/> to create the basis from.</param>
/// <param name="origin">The origin vector, or column index 3.</param>
public Transform3D(Quaternion quaternion, Vector3 origin)
{
@@ -333,9 +341,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a transformation matrix from the given basis and origin vector.
+ /// Constructs a transformation matrix from the given <paramref name="basis"/> and
+ /// <paramref name="origin"/> vector.
/// </summary>
- /// <param name="basis">The <see cref="Godot.Basis"/> to create the basis from.</param>
+ /// <param name="basis">The <see cref="Basis"/> to create the basis from.</param>
/// <param name="origin">The origin vector, or column index 3.</param>
public Transform3D(Basis basis, Vector3 origin)
{
@@ -343,6 +352,14 @@ namespace Godot
this.origin = origin;
}
+ /// <summary>
+ /// Composes these two transformation matrices by multiplying them
+ /// together. This has the effect of transforming the second transform
+ /// (the child) by the first transform (the parent).
+ /// </summary>
+ /// <param name="left">The parent transform.</param>
+ /// <param name="right">The child transform.</param>
+ /// <returns>The composed transform.</returns>
public static Transform3D operator *(Transform3D left, Transform3D right)
{
left.origin = left.Xform(right.origin);
@@ -350,16 +367,40 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left transform.</param>
+ /// <param name="right">The right transform.</param>
+ /// <returns>Whether or not the transforms are exactly equal.</returns>
public static bool operator ==(Transform3D left, Transform3D right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left transform.</param>
+ /// <param name="right">The right transform.</param>
+ /// <returns>Whether or not the transforms are not equal.</returns>
public static bool operator !=(Transform3D left, Transform3D right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transform is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the transform and the object are exactly equal.</returns>
public override bool Equals(object obj)
{
if (obj is Transform3D)
@@ -370,14 +411,21 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the transforms are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other transform to compare.</param>
+ /// <returns>Whether or not the matrices are exactly equal.</returns>
public bool Equals(Transform3D other)
{
return basis.Equals(other.basis) && origin.Equals(other.origin);
}
/// <summary>
- /// Returns true if this transform and `other` are approximately equal, by running
- /// <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
+ /// Returns <see langword="true"/> if this transform and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Vector3.IsEqualApprox(Vector3)"/> on each component.
/// </summary>
/// <param name="other">The other transform to compare.</param>
/// <returns>Whether or not the matrices are approximately equal.</returns>
@@ -386,16 +434,28 @@ namespace Godot
return basis.IsEqualApprox(other.basis) && origin.IsEqualApprox(other.origin);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Transform3D"/>.
+ /// </summary>
+ /// <returns>A hash code for this transform.</returns>
public override int GetHashCode()
{
return basis.GetHashCode() ^ origin.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Transform3D"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this transform.</returns>
public override string ToString()
{
return $"[X: {basis.x}, Y: {basis.y}, Z: {basis.z}, O: {origin}]";
}
+ /// <summary>
+ /// Converts this <see cref="Transform3D"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this transform.</returns>
public string ToString(string format)
{
return $"[X: {basis.x.ToString(format)}, Y: {basis.y.ToString(format)}, Z: {basis.z.ToString(format)}, O: {origin.ToString(format)}]";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
index be01674568..eae8927ceb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
@@ -8,7 +8,7 @@ namespace Godot
public class UnhandledExceptionArgs
{
/// <summary>
- /// Exception object
+ /// Exception object.
/// </summary>
public Exception Exception { get; private set; }
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index 8bb5e90a68..1f5282e88f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -17,27 +17,40 @@ namespace Godot
{
/// <summary>
/// Enumerated index values for the axes.
- /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
Y
}
/// <summary>
- /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public real_t x;
+
/// <summary>
- /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public real_t y;
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the given the <paramref name="index"/> is not 0 or 1.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>.
+ /// </value>
public real_t this[int index]
{
get
@@ -97,7 +110,7 @@ namespace Godot
/// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
///
/// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
- /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>.
/// </summary>
/// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
@@ -126,9 +139,9 @@ namespace Godot
}
/// <summary>
- /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>.
/// </summary>
- /// <returns>The `x` component divided by the `y` component.</returns>
+ /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns>
public real_t Aspect()
{
return x / y;
@@ -155,7 +168,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components clamped between the
- /// components of `min` and `max` using
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
@@ -171,21 +184,22 @@ namespace Godot
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns the cross product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector.</param>
+ /// <param name="with">The other vector.</param>
/// <returns>The cross product value.</returns>
- public real_t Cross(Vector2 b)
+ public real_t Cross(Vector2 with)
{
- return x * b.y - y * b.x;
+ return (x * with.y) - (y * with.x);
}
/// <summary>
- /// Performs a cubic interpolation between vectors `preA`, this vector, `b`, and `postB`, by the given amount `t`.
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
/// </summary>
/// <param name="b">The destination vector.</param>
/// <param name="preA">A vector before this vector.</param>
- /// <param name="postB">A vector after `b`.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight)
@@ -199,24 +213,26 @@ namespace Godot
real_t t2 = t * t;
real_t t3 = t2 * t;
- return 0.5f * (p1 * 2.0f +
- (-p0 + p2) * t +
- (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
- (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+ return 0.5f * (
+ (p1 * 2.0f) +
+ ((-p0 + p2) * t) +
+ (((2.0f * p0) - (5.0f * p1) + (4 * p2) - p3) * t2) +
+ ((-p0 + (3.0f * p1) - (3.0f * p2) + p3) * t3)
+ );
}
/// <summary>
- /// Returns the normalized vector pointing from this vector to `b`.
+ /// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
- /// <param name="b">The other vector to point towards.</param>
- /// <returns>The direction from this vector to `b`.</returns>
- public Vector2 DirectionTo(Vector2 b)
+ /// <param name="to">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to <paramref name="to"/>.</returns>
+ public Vector2 DirectionTo(Vector2 to)
{
- return new Vector2(b.x - x, b.y - y).Normalized();
+ return new Vector2(to.x - x, to.y - y).Normalized();
}
/// <summary>
- /// Returns the squared distance between this vector and `to`.
+ /// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
/// </summary>
@@ -228,7 +244,7 @@ namespace Godot
}
/// <summary>
- /// Returns the distance between this vector and `to`.
+ /// Returns the distance between this vector and <paramref name="to"/>.
/// </summary>
/// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
@@ -238,13 +254,13 @@ namespace Godot
}
/// <summary>
- /// Returns the dot product of this vector and `with`.
+ /// Returns the dot product of this vector and <paramref name="with"/>.
/// </summary>
/// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector2 with)
{
- return x * with.x + y * with.y;
+ return (x * with.x) + (y * with.y);
}
/// <summary>
@@ -257,7 +273,7 @@ namespace Godot
}
/// <summary>
- /// Returns the inverse of this vector. This is the same as `new Vector2(1 / v.x, 1 / v.y)`.
+ /// Returns the inverse of this vector. This is the same as <c>new Vector2(1 / v.x, 1 / v.y)</c>.
/// </summary>
/// <returns>The inverse of this vector.</returns>
public Vector2 Inverse()
@@ -266,9 +282,9 @@ namespace Godot
}
/// <summary>
- /// Returns true if the vector is normalized, and false otherwise.
+ /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool indicating whether or not the vector is normalized.</returns>
+ /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
@@ -277,10 +293,11 @@ namespace Godot
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
+ /// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
- return Mathf.Sqrt(x * x + y * y);
+ return Mathf.Sqrt((x * x) + (y * y));
}
/// <summary>
@@ -291,12 +308,12 @@ namespace Godot
/// <returns>The squared length of this vector.</returns>
public real_t LengthSquared()
{
- return x * x + y * y;
+ return (x * x) + (y * y);
}
/// <summary>
/// Returns the result of the linear interpolation between
- /// this vector and `to` by amount `weight`.
+ /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -312,10 +329,12 @@ namespace Godot
/// <summary>
/// Returns the result of the linear interpolation between
- /// this vector and `to` by the vector amount `weight`.
+ /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
- /// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <param name="weight">
+ /// A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.
+ /// </param>
/// <returns>The resulting vector of the interpolation.</returns>
public Vector2 Lerp(Vector2 to, Vector2 weight)
{
@@ -327,7 +346,7 @@ namespace Godot
}
/// <summary>
- /// Returns the vector with a maximum length by limiting its length to `length`.
+ /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>.
/// </summary>
/// <param name="length">The length to limit to.</param>
/// <returns>The vector with its length limited.</returns>
@@ -346,55 +365,61 @@ namespace Godot
}
/// <summary>
- /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
- /// <returns>The index of the largest axis.</returns>
- public Axis MaxAxis()
+ /// <returns>The index of the highest axis.</returns>
+ public Axis MaxAxisIndex()
{
return x < y ? Axis.Y : Axis.X;
}
/// <summary>
- /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.Y"/>.
/// </summary>
- /// <returns>The index of the smallest axis.</returns>
- public Axis MinAxis()
+ /// <returns>The index of the lowest axis.</returns>
+ public Axis MinAxisIndex()
{
return x < y ? Axis.X : Axis.Y;
}
/// <summary>
- /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// Moves this vector toward <paramref name="to"/> by the fixed <paramref name="delta"/> amount.
/// </summary>
/// <param name="to">The vector to move towards.</param>
/// <param name="delta">The amount to move towards by.</param>
/// <returns>The resulting vector.</returns>
public Vector2 MoveToward(Vector2 to, real_t delta)
{
- var v = this;
- var vd = to - v;
- var len = vd.Length();
- return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ Vector2 v = this;
+ Vector2 vd = to - v;
+ real_t len = vd.Length();
+ if (len <= delta || len < Mathf.Epsilon)
+ return to;
+
+ return v + (vd / len * delta);
}
/// <summary>
- /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
/// </summary>
/// <returns>A normalized version of the vector.</returns>
public Vector2 Normalized()
{
- var v = this;
+ Vector2 v = this;
v.Normalize();
return v;
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="mod"/>.
/// </summary>
/// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>.
+ /// </returns>
public Vector2 PosMod(real_t mod)
{
Vector2 v;
@@ -404,10 +429,13 @@ namespace Godot
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="modv"/>'s components.
/// </summary>
/// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components.
+ /// </returns>
public Vector2 PosMod(Vector2 modv)
{
Vector2 v;
@@ -417,7 +445,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector projected onto another vector `b`.
+ /// Returns this vector projected onto another vector <paramref name="onNormal"/>.
/// </summary>
/// <param name="onNormal">The vector to project onto.</param>
/// <returns>The projected vector.</returns>
@@ -427,7 +455,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// Returns this vector reflected from a plane defined by the given <paramref name="normal"/>.
/// </summary>
/// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
/// <returns>The reflected vector.</returns>
@@ -439,11 +467,11 @@ namespace Godot
throw new ArgumentException("Argument is not normalized", nameof(normal));
}
#endif
- return 2 * Dot(normal) * normal - this;
+ return (2 * Dot(normal) * normal) - this;
}
/// <summary>
- /// Rotates this vector by `phi` radians.
+ /// Rotates this vector by <paramref name="phi"/> radians.
/// </summary>
/// <param name="phi">The angle to rotate by, in radians.</param>
/// <returns>The rotated vector.</returns>
@@ -471,7 +499,7 @@ namespace Godot
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
/// </summary>
- /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector2 Sign()
{
Vector2 v;
@@ -482,7 +510,7 @@ namespace Godot
/// <summary>
/// Returns the result of the spherical linear interpolation between
- /// this vector and `to` by amount `weight`.
+ /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
/// Note: Both vectors must be normalized.
/// </summary>
@@ -498,24 +526,24 @@ namespace Godot
}
if (!to.IsNormalized())
{
- throw new InvalidOperationException("Vector2.Slerp: `to` is not normalized.");
+ throw new InvalidOperationException($"Vector2.Slerp: `{nameof(to)}` is not normalized.");
}
#endif
return Rotated(AngleTo(to) * weight);
}
/// <summary>
- /// Returns this vector slid along a plane defined by the given normal.
+ /// Returns this vector slid along a plane defined by the given <paramref name="normal"/>.
/// </summary>
/// <param name="normal">The normal vector defining the plane to slide on.</param>
/// <returns>The slid vector.</returns>
public Vector2 Slide(Vector2 normal)
{
- return this - normal * Dot(normal);
+ return this - (normal * Dot(normal));
}
/// <summary>
- /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
/// This can also be used to round to an arbitrary number of decimals.
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
@@ -546,40 +574,40 @@ namespace Godot
private static readonly Vector2 _left = new Vector2(-1, 0);
/// <summary>
- /// Zero vector, a vector with all components set to `0`.
+ /// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
- /// <value>Equivalent to `new Vector2(0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2(0, 0)</c>.</value>
public static Vector2 Zero { get { return _zero; } }
/// <summary>
- /// One vector, a vector with all components set to `1`.
+ /// One vector, a vector with all components set to <c>1</c>.
/// </summary>
- /// <value>Equivalent to `new Vector2(1, 1)`</value>
+ /// <value>Equivalent to <c>new Vector2(1, 1)</c>.</value>
public static Vector2 One { get { return _one; } }
/// <summary>
- /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>.
/// </summary>
- /// <value>Equivalent to `new Vector2(Mathf.Inf, Mathf.Inf)`</value>
+ /// <value>Equivalent to <c>new Vector2(Mathf.Inf, Mathf.Inf)</c>.</value>
public static Vector2 Inf { get { return _inf; } }
/// <summary>
/// Up unit vector. Y is down in 2D, so this vector points -Y.
/// </summary>
- /// <value>Equivalent to `new Vector2(0, -1)`</value>
+ /// <value>Equivalent to <c>new Vector2(0, -1)</c>.</value>
public static Vector2 Up { get { return _up; } }
/// <summary>
/// Down unit vector. Y is down in 2D, so this vector points +Y.
/// </summary>
- /// <value>Equivalent to `new Vector2(0, 1)`</value>
+ /// <value>Equivalent to <c>new Vector2(0, 1)</c>.</value>
public static Vector2 Down { get { return _down; } }
/// <summary>
/// Right unit vector. Represents the direction of right.
/// </summary>
- /// <value>Equivalent to `new Vector2(1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2(1, 0)</c>.</value>
public static Vector2 Right { get { return _right; } }
/// <summary>
/// Left unit vector. Represents the direction of left.
/// </summary>
- /// <value>Equivalent to `new Vector2(-1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2(-1, 0)</c>.</value>
public static Vector2 Left { get { return _left; } }
/// <summary>
@@ -603,6 +631,24 @@ namespace Godot
y = v.y;
}
+ /// <summary>
+ /// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing
+ /// <c>Vector2(Mathf.Cos(angle), Mathf.Sin(angle))</c> or <c>Vector2.Right.Rotated(angle)</c>.
+ /// </summary>
+ /// <param name="angle">Angle of the vector, in radians.</param>
+ /// <returns>The resulting vector.</returns>
+ public static Vector2 FromAngle(real_t angle)
+ {
+ return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
+ }
+
+ /// <summary>
+ /// Adds each component of the <see cref="Vector2"/>
+ /// with the components of the given <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
public static Vector2 operator +(Vector2 left, Vector2 right)
{
left.x += right.x;
@@ -610,6 +656,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector2"/>
+ /// by the components of the given <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
public static Vector2 operator -(Vector2 left, Vector2 right)
{
left.x -= right.x;
@@ -617,6 +670,15 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector2"/>.
+ /// This is the same as writing <c>new Vector2(-v.x, -v.y)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// With floats, the number zero can be either positive or negative.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
public static Vector2 operator -(Vector2 vec)
{
vec.x = -vec.x;
@@ -624,6 +686,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2 operator *(Vector2 vec, real_t scale)
{
vec.x *= scale;
@@ -631,6 +700,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2 operator *(real_t scale, Vector2 vec)
{
vec.x *= scale;
@@ -638,6 +714,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2"/>
+ /// by the components of the given <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2 operator *(Vector2 left, Vector2 right)
{
left.x *= right.x;
@@ -645,6 +728,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
public static Vector2 operator /(Vector2 vec, real_t divisor)
{
vec.x /= divisor;
@@ -652,6 +742,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Vector2"/>
+ /// by the components of the given <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
public static Vector2 operator /(Vector2 vec, Vector2 divisorv)
{
vec.x /= divisorv.x;
@@ -659,6 +756,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector2"/>
+ /// with the components of the given <see cref="real_t"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(real_t)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector2(10, -20) % 7); // Prints "(3, -6)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector2 operator %(Vector2 vec, real_t divisor)
{
vec.x %= divisor;
@@ -666,6 +779,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector2"/>
+ /// with the components of the given <see cref="Vector2"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(Vector2)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector2(10, -20) % new Vector2(7, 8)); // Prints "(3, -4)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector2 operator %(Vector2 vec, Vector2 divisorv)
{
vec.x %= divisorv.x;
@@ -673,16 +802,43 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
public static bool operator ==(Vector2 left, Vector2 right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector2 left, Vector2 right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Compares two <see cref="Vector2"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector2 left, Vector2 right)
{
if (left.x == right.x)
@@ -692,6 +848,17 @@ namespace Godot
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector2 left, Vector2 right)
{
if (left.x == right.x)
@@ -701,24 +868,54 @@ namespace Godot
return left.x > right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector2 left, Vector2 right)
{
if (left.x == right.x)
{
return left.y <= right.y;
}
- return left.x <= right.x;
+ return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector2 left, Vector2 right)
{
if (left.x == right.x)
{
return left.y >= right.y;
}
- return left.x >= right.x;
+ return left.x > right.x;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector2)
@@ -728,14 +925,21 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
public bool Equals(Vector2 other)
{
return x == other.x && y == other.y;
}
/// <summary>
- /// Returns true if this vector and `other` are approximately equal, by running
- /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
/// </summary>
/// <param name="other">The other vector to compare.</param>
/// <returns>Whether or not the vectors are approximately equal.</returns>
@@ -744,16 +948,28 @@ namespace Godot
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector2"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Vector2"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y})";
}
+ /// <summary>
+ /// Converts this <see cref="Vector2"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)})";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
index 959f262f52..9b51de5c8c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -17,27 +17,40 @@ namespace Godot
{
/// <summary>
/// Enumerated index values for the axes.
- /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
Y
}
/// <summary>
- /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public int x;
+
/// <summary>
- /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public int y;
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`.</value>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the given the <paramref name="index"/> is not 0 or 1.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>.
+ /// </value>
public int this[int index]
{
get
@@ -81,7 +94,7 @@ namespace Godot
/// Returns this vector's angle with respect to the X axis, or (1, 0) vector, in radians.
///
/// Equivalent to the result of <see cref="Mathf.Atan2(real_t, real_t)"/> when
- /// called with the vector's `y` and `x` as parameters: `Mathf.Atan2(v.y, v.x)`.
+ /// called with the vector's <see cref="y"/> and <see cref="x"/> as parameters: <c>Mathf.Atan2(v.y, v.x)</c>.
/// </summary>
/// <returns>The angle of this vector, in radians.</returns>
public real_t Angle()
@@ -110,9 +123,9 @@ namespace Godot
}
/// <summary>
- /// Returns the aspect ratio of this vector, the ratio of `x` to `y`.
+ /// Returns the aspect ratio of this vector, the ratio of <see cref="x"/> to <see cref="y"/>.
/// </summary>
- /// <returns>The `x` component divided by the `y` component.</returns>
+ /// <returns>The <see cref="x"/> component divided by the <see cref="y"/> component.</returns>
public real_t Aspect()
{
return x / (real_t)y;
@@ -120,7 +133,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components clamped between the
- /// components of `min` and `max` using
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(int, int, int)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
@@ -136,50 +149,51 @@ namespace Godot
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns the cross product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector.</param>
+ /// <param name="with">The other vector.</param>
/// <returns>The cross product vector.</returns>
- public int Cross(Vector2i b)
+ public int Cross(Vector2i with)
{
- return x * b.y - y * b.x;
+ return x * with.y - y * with.x;
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The squared distance between the two vectors.</returns>
- public int DistanceSquaredTo(Vector2i b)
+ public int DistanceSquaredTo(Vector2i to)
{
- return (b - this).LengthSquared();
+ return (to - this).LengthSquared();
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="to"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector2i b)
+ public real_t DistanceTo(Vector2i to)
{
- return (b - this).Length();
+ return (to - this).Length();
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
- public int Dot(Vector2i b)
+ public int Dot(Vector2i with)
{
- return x * b.x + y * b.y;
+ return x * with.x + y * with.y;
}
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
+ /// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
@@ -204,30 +218,33 @@ namespace Godot
}
/// <summary>
- /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
- /// <returns>The index of the largest axis.</returns>
- public Axis MaxAxis()
+ /// <returns>The index of the highest axis.</returns>
+ public Axis MaxAxisIndex()
{
return x < y ? Axis.Y : Axis.X;
}
/// <summary>
- /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.Y"/>.
/// </summary>
- /// <returns>The index of the smallest axis.</returns>
- public Axis MinAxis()
+ /// <returns>The index of the lowest axis.</returns>
+ public Axis MinAxisIndex()
{
return x < y ? Axis.X : Axis.Y;
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
+ /// and <paramref name="mod"/>.
/// </summary>
/// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>.
+ /// </returns>
public Vector2i PosMod(int mod)
{
Vector2i v = this;
@@ -237,10 +254,13 @@ namespace Godot
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
+ /// and <paramref name="modv"/>'s components.
/// </summary>
/// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components.
+ /// </returns>
public Vector2i PosMod(Vector2i modv)
{
Vector2i v = this;
@@ -254,7 +274,7 @@ namespace Godot
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
/// </summary>
- /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector2i Sign()
{
Vector2i v = this;
@@ -283,35 +303,35 @@ namespace Godot
private static readonly Vector2i _left = new Vector2i(-1, 0);
/// <summary>
- /// Zero vector, a vector with all components set to `0`.
+ /// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
- /// <value>Equivalent to `new Vector2i(0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2i(0, 0)</c>.</value>
public static Vector2i Zero { get { return _zero; } }
/// <summary>
- /// One vector, a vector with all components set to `1`.
+ /// One vector, a vector with all components set to <c>1</c>.
/// </summary>
- /// <value>Equivalent to `new Vector2i(1, 1)`</value>
+ /// <value>Equivalent to <c>new Vector2i(1, 1)</c>.</value>
public static Vector2i One { get { return _one; } }
/// <summary>
/// Up unit vector. Y is down in 2D, so this vector points -Y.
/// </summary>
- /// <value>Equivalent to `new Vector2i(0, -1)`</value>
+ /// <value>Equivalent to <c>new Vector2i(0, -1)</c>.</value>
public static Vector2i Up { get { return _up; } }
/// <summary>
/// Down unit vector. Y is down in 2D, so this vector points +Y.
/// </summary>
- /// <value>Equivalent to `new Vector2i(0, 1)`</value>
+ /// <value>Equivalent to <c>new Vector2i(0, 1)</c>.</value>
public static Vector2i Down { get { return _down; } }
/// <summary>
/// Right unit vector. Represents the direction of right.
/// </summary>
- /// <value>Equivalent to `new Vector2i(1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2i(1, 0)</c>.</value>
public static Vector2i Right { get { return _right; } }
/// <summary>
/// Left unit vector. Represents the direction of left.
/// </summary>
- /// <value>Equivalent to `new Vector2i(-1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector2i(-1, 0)</c>.</value>
public static Vector2i Left { get { return _left; } }
/// <summary>
@@ -346,6 +366,13 @@ namespace Godot
this.y = Mathf.RoundToInt(v.y);
}
+ /// <summary>
+ /// Adds each component of the <see cref="Vector2i"/>
+ /// with the components of the given <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
public static Vector2i operator +(Vector2i left, Vector2i right)
{
left.x += right.x;
@@ -353,6 +380,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector2i"/>
+ /// by the components of the given <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
public static Vector2i operator -(Vector2i left, Vector2i right)
{
left.x -= right.x;
@@ -360,6 +394,14 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector2i"/>.
+ /// This is the same as writing <c>new Vector2i(-v.x, -v.y)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
public static Vector2i operator -(Vector2i vec)
{
vec.x = -vec.x;
@@ -367,6 +409,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2i operator *(Vector2i vec, int scale)
{
vec.x *= scale;
@@ -374,6 +423,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2i operator *(int scale, Vector2i vec)
{
vec.x *= scale;
@@ -381,6 +437,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2i"/>
+ /// by the components of the given <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector2i operator *(Vector2i left, Vector2i right)
{
left.x *= right.x;
@@ -388,6 +451,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector2i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
public static Vector2i operator /(Vector2i vec, int divisor)
{
vec.x /= divisor;
@@ -395,6 +465,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Vector2i"/>
+ /// by the components of the given <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
public static Vector2i operator /(Vector2i vec, Vector2i divisorv)
{
vec.x /= divisorv.x;
@@ -402,6 +479,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector2i"/>
+ /// with the components of the given <see langword="int"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(int)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector2i(10, -20) % 7); // Prints "(3, -6)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector2i operator %(Vector2i vec, int divisor)
{
vec.x %= divisor;
@@ -409,6 +502,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector2i"/>
+ /// with the components of the given <see cref="Vector2i"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(Vector2i)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector2i(10, -20) % new Vector2i(7, 8)); // Prints "(3, -4)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector2i operator %(Vector2i vec, Vector2i divisorv)
{
vec.x %= divisorv.x;
@@ -416,6 +525,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Performs a bitwise AND operation with this <see cref="Vector2i"/>
+ /// and the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The vector to AND with.</param>
+ /// <param name="and">The integer to AND with.</param>
+ /// <returns>The result of the bitwise AND.</returns>
public static Vector2i operator &(Vector2i vec, int and)
{
vec.x &= and;
@@ -423,6 +539,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Performs a bitwise AND operation with this <see cref="Vector2i"/>
+ /// and the given <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="vec">The left vector to AND with.</param>
+ /// <param name="andv">The right vector to AND with.</param>
+ /// <returns>The result of the bitwise AND.</returns>
public static Vector2i operator &(Vector2i vec, Vector2i andv)
{
vec.x &= andv.x;
@@ -430,62 +553,132 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
public static bool operator ==(Vector2i left, Vector2i right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector2i left, Vector2i right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Compares two <see cref="Vector2i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector2i left, Vector2i right)
{
- if (left.x.Equals(right.x))
+ if (left.x == right.x)
{
return left.y < right.y;
}
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector2i left, Vector2i right)
{
- if (left.x.Equals(right.x))
+ if (left.x == right.x)
{
return left.y > right.y;
}
return left.x > right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector2i left, Vector2i right)
{
- if (left.x.Equals(right.x))
+ if (left.x == right.x)
{
return left.y <= right.y;
}
- return left.x <= right.x;
+ return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector2i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector2i left, Vector2i right)
{
- if (left.x.Equals(right.x))
+ if (left.x == right.x)
{
return left.y >= right.y;
}
- return left.x >= right.x;
+ return left.x > right.x;
}
+ /// <summary>
+ /// Converts this <see cref="Vector2i"/> to a <see cref="Vector2"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
public static implicit operator Vector2(Vector2i value)
{
return new Vector2(value.x, value.y);
}
+ /// <summary>
+ /// Converts a <see cref="Vector2"/> to a <see cref="Vector2i"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
public static explicit operator Vector2i(Vector2 value)
{
return new Vector2i(value);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is equal
+ /// to the given object (<see paramref="obj"/>).
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector2i)
@@ -496,21 +689,38 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
public bool Equals(Vector2i other)
{
return x == other.x && y == other.y;
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector2i"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Vector2i"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y})";
}
+ /// <summary>
+ /// Converts this <see cref="Vector2i"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)})";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index bdf64159e9..433a5d9dc9 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -17,32 +17,50 @@ namespace Godot
{
/// <summary>
/// Enumerated index values for the axes.
- /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
Y,
+ /// <summary>
+ /// The vector's Z axis.
+ /// </summary>
Z
}
/// <summary>
- /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public real_t x;
+
/// <summary>
- /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public real_t y;
+
/// <summary>
- /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public real_t z;
/// <summary>
/// Access vector components using their index.
/// </summary>
- /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the given the <paramref name="index"/> is not 0, 1 or 2.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>,
+ /// <c>[2]</c> is equivalent to <see cref="z"/>.
+ /// </value>
public real_t this[int index]
{
get
@@ -135,7 +153,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components clamped between the
- /// components of `min` and `max` using
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
@@ -152,27 +170,27 @@ namespace Godot
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns the cross product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector.</param>
+ /// <param name="with">The other vector.</param>
/// <returns>The cross product vector.</returns>
- public Vector3 Cross(Vector3 b)
+ public Vector3 Cross(Vector3 with)
{
return new Vector3
(
- y * b.z - z * b.y,
- z * b.x - x * b.z,
- x * b.y - y * b.x
+ (y * with.z) - (z * with.y),
+ (z * with.x) - (x * with.z),
+ (x * with.y) - (y * with.x)
);
}
/// <summary>
- /// Performs a cubic interpolation between vectors `preA`, this vector,
- /// `b`, and `postB`, by the given amount `t`.
+ /// Performs a cubic interpolation between vectors <paramref name="preA"/>, this vector,
+ /// <paramref name="b"/>, and <paramref name="postB"/>, by the given amount <paramref name="weight"/>.
/// </summary>
/// <param name="b">The destination vector.</param>
/// <param name="preA">A vector before this vector.</param>
- /// <param name="postB">A vector after `b`.</param>
+ /// <param name="postB">A vector after <paramref name="b"/>.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
/// <returns>The interpolated vector.</returns>
public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight)
@@ -187,52 +205,53 @@ namespace Godot
real_t t3 = t2 * t;
return 0.5f * (
- p1 * 2.0f + (-p0 + p2) * t +
- (2.0f * p0 - 5.0f * p1 + 4f * p2 - p3) * t2 +
- (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3
- );
+ (p1 * 2.0f) + ((-p0 + p2) * t) +
+ (((2.0f * p0) - (5.0f * p1) + (4f * p2) - p3) * t2) +
+ ((-p0 + (3.0f * p1) - (3.0f * p2) + p3) * t3)
+ );
}
/// <summary>
- /// Returns the normalized vector pointing from this vector to `b`.
+ /// Returns the normalized vector pointing from this vector to <paramref name="to"/>.
/// </summary>
- /// <param name="b">The other vector to point towards.</param>
- /// <returns>The direction from this vector to `b`.</returns>
- public Vector3 DirectionTo(Vector3 b)
+ /// <param name="to">The other vector to point towards.</param>
+ /// <returns>The direction from this vector to <paramref name="to"/>.</returns>
+ public Vector3 DirectionTo(Vector3 to)
{
- return new Vector3(b.x - x, b.y - y, b.z - z).Normalized();
+ return new Vector3(to.x - x, to.y - y, to.z - z).Normalized();
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The squared distance between the two vectors.</returns>
- public real_t DistanceSquaredTo(Vector3 b)
+ public real_t DistanceSquaredTo(Vector3 to)
{
- return (b - this).LengthSquared();
+ return (to - this).LengthSquared();
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="to"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <seealso cref="DistanceSquaredTo(Vector3)"/>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector3 b)
+ public real_t DistanceTo(Vector3 to)
{
- return (b - this).Length();
+ return (to - this).Length();
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
- public real_t Dot(Vector3 b)
+ public real_t Dot(Vector3 with)
{
- return x * b.x + y * b.y + z * b.z;
+ return (x * with.x) + (y * with.y) + (z * with.z);
}
/// <summary>
@@ -245,7 +264,7 @@ namespace Godot
}
/// <summary>
- /// Returns the inverse of this vector. This is the same as `new Vector3(1 / v.x, 1 / v.y, 1 / v.z)`.
+ /// Returns the inverse of this vector. This is the same as <c>new Vector3(1 / v.x, 1 / v.y, 1 / v.z)</c>.
/// </summary>
/// <returns>The inverse of this vector.</returns>
public Vector3 Inverse()
@@ -254,9 +273,9 @@ namespace Godot
}
/// <summary>
- /// Returns true if the vector is normalized, and false otherwise.
+ /// Returns <see langword="true"/> if the vector is normalized, and <see langword="false"/> otherwise.
/// </summary>
- /// <returns>A bool indicating whether or not the vector is normalized.</returns>
+ /// <returns>A <see langword="bool"/> indicating whether or not the vector is normalized.</returns>
public bool IsNormalized()
{
return Mathf.Abs(LengthSquared() - 1.0f) < Mathf.Epsilon;
@@ -265,6 +284,7 @@ namespace Godot
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
+ /// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
@@ -292,7 +312,7 @@ namespace Godot
/// <summary>
/// Returns the result of the linear interpolation between
- /// this vector and `to` by amount `weight`.
+ /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -309,7 +329,7 @@ namespace Godot
/// <summary>
/// Returns the result of the linear interpolation between
- /// this vector and `to` by the vector amount `weight`.
+ /// this vector and <paramref name="to"/> by the vector amount <paramref name="weight"/>.
/// </summary>
/// <param name="to">The destination vector for interpolation.</param>
/// <param name="weight">A vector with components on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
@@ -325,7 +345,7 @@ namespace Godot
}
/// <summary>
- /// Returns the vector with a maximum length by limiting its length to `length`.
+ /// Returns the vector with a maximum length by limiting its length to <paramref name="length"/>.
/// </summary>
/// <param name="length">The length to limit to.</param>
/// <returns>The vector with its length limited.</returns>
@@ -344,69 +364,75 @@ namespace Godot
}
/// <summary>
- /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
- /// <returns>The index of the largest axis.</returns>
- public Axis MaxAxis()
+ /// <returns>The index of the highest axis.</returns>
+ public Axis MaxAxisIndex()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
/// <summary>
- /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.Z"/>.
/// </summary>
- /// <returns>The index of the smallest axis.</returns>
- public Axis MinAxis()
+ /// <returns>The index of the lowest axis.</returns>
+ public Axis MinAxisIndex()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
/// <summary>
- /// Moves this vector toward `to` by the fixed `delta` amount.
+ /// Moves this vector toward <paramref name="to"/> by the fixed <paramref name="delta"/> amount.
/// </summary>
/// <param name="to">The vector to move towards.</param>
/// <param name="delta">The amount to move towards by.</param>
/// <returns>The resulting vector.</returns>
public Vector3 MoveToward(Vector3 to, real_t delta)
{
- var v = this;
- var vd = to - v;
- var len = vd.Length();
- return len <= delta || len < Mathf.Epsilon ? to : v + vd / len * delta;
+ Vector3 v = this;
+ Vector3 vd = to - v;
+ real_t len = vd.Length();
+ if (len <= delta || len < Mathf.Epsilon)
+ return to;
+
+ return v + (vd / len * delta);
}
/// <summary>
- /// Returns the vector scaled to unit length. Equivalent to `v / v.Length()`.
+ /// Returns the vector scaled to unit length. Equivalent to <c>v / v.Length()</c>.
/// </summary>
/// <returns>A normalized version of the vector.</returns>
public Vector3 Normalized()
{
- var v = this;
+ Vector3 v = this;
v.Normalize();
return v;
}
/// <summary>
- /// Returns the outer product with `b`.
+ /// Returns the outer product with <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector.</param>
+ /// <param name="with">The other vector.</param>
/// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns>
- public Basis Outer(Vector3 b)
+ public Basis Outer(Vector3 with)
{
return new Basis(
- x * b.x, x * b.y, x * b.z,
- y * b.x, y * b.y, y * b.z,
- z * b.x, z * b.y, z * b.z
+ x * with.x, x * with.y, x * with.z,
+ y * with.x, y * with.y, y * with.z,
+ z * with.x, z * with.y, z * with.z
);
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `mod`.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="mod"/>.
/// </summary>
/// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `mod`.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="mod"/>.
+ /// </returns>
public Vector3 PosMod(real_t mod)
{
Vector3 v;
@@ -417,10 +443,13 @@ namespace Godot
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components and `modv`'s components.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(real_t, real_t)"/> of this vector's components
+ /// and <paramref name="modv"/>'s components.
/// </summary>
/// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by `modv`'s components.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(real_t, real_t)"/> by <paramref name="modv"/>'s components.
+ /// </returns>
public Vector3 PosMod(Vector3 modv)
{
Vector3 v;
@@ -431,7 +460,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector projected onto another vector `b`.
+ /// Returns this vector projected onto another vector <paramref name="onNormal"/>.
/// </summary>
/// <param name="onNormal">The vector to project onto.</param>
/// <returns>The projected vector.</returns>
@@ -441,7 +470,7 @@ namespace Godot
}
/// <summary>
- /// Returns this vector reflected from a plane defined by the given `normal`.
+ /// Returns this vector reflected from a plane defined by the given <paramref name="normal"/>.
/// </summary>
/// <param name="normal">The normal vector defining the plane to reflect from. Must be normalized.</param>
/// <returns>The reflected vector.</returns>
@@ -453,12 +482,12 @@ namespace Godot
throw new ArgumentException("Argument is not normalized", nameof(normal));
}
#endif
- return 2.0f * Dot(normal) * normal - this;
+ return (2.0f * Dot(normal) * normal) - this;
}
/// <summary>
- /// Rotates this vector around a given `axis` vector by `phi` radians.
- /// The `axis` vector must be a normalized vector.
+ /// Rotates this vector around a given <paramref name="axis"/> vector by <paramref name="phi"/> radians.
+ /// The <paramref name="axis"/> vector must be a normalized vector.
/// </summary>
/// <param name="axis">The vector to rotate around. Must be normalized.</param>
/// <param name="phi">The angle to rotate by, in radians.</param>
@@ -489,7 +518,7 @@ namespace Godot
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(real_t)"/> on each component.
/// </summary>
- /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector3 Sign()
{
Vector3 v;
@@ -503,7 +532,7 @@ namespace Godot
/// Returns the signed angle to the given vector, in radians.
/// The sign of the angle is positive in a counter-clockwise
/// direction and negative in a clockwise direction when viewed
- /// from the side specified by the `axis`.
+ /// from the side specified by the <paramref name="axis"/>.
/// </summary>
/// <param name="to">The other vector to compare this vector to.</param>
/// <param name="axis">The reference axis to use for the angle sign.</param>
@@ -518,7 +547,7 @@ namespace Godot
/// <summary>
/// Returns the result of the spherical linear interpolation between
- /// this vector and `to` by amount `weight`.
+ /// this vector and <paramref name="to"/> by amount <paramref name="weight"/>.
///
/// Note: Both vectors must be normalized.
/// </summary>
@@ -534,7 +563,7 @@ namespace Godot
}
if (!to.IsNormalized())
{
- throw new InvalidOperationException("Vector3.Slerp: `to` is not normalized.");
+ throw new InvalidOperationException($"Vector3.Slerp: `{nameof(to)}` is not normalized.");
}
#endif
real_t theta = AngleTo(to);
@@ -542,17 +571,17 @@ namespace Godot
}
/// <summary>
- /// Returns this vector slid along a plane defined by the given normal.
+ /// Returns this vector slid along a plane defined by the given <paramref name="normal"/>.
/// </summary>
/// <param name="normal">The normal vector defining the plane to slide on.</param>
/// <returns>The slid vector.</returns>
public Vector3 Slide(Vector3 normal)
{
- return this - normal * Dot(normal);
+ return this - (normal * Dot(normal));
}
/// <summary>
- /// Returns this vector with each component snapped to the nearest multiple of `step`.
+ /// Returns this vector with each component snapped to the nearest multiple of <paramref name="step"/>.
/// This can also be used to round to an arbitrary number of decimals.
/// </summary>
/// <param name="step">A vector value representing the step size to snap to.</param>
@@ -570,10 +599,10 @@ namespace Godot
/// <summary>
/// Returns a diagonal matrix with the vector as main diagonal.
///
- /// This is equivalent to a Basis with no rotation or shearing and
+ /// This is equivalent to a <see cref="Basis"/> with no rotation or shearing and
/// this vector's components set as the scale.
/// </summary>
- /// <returns>A Basis with the vector as its main diagonal.</returns>
+ /// <returns>A <see cref="Basis"/> with the vector as its main diagonal.</returns>
public Basis ToDiagonalMatrix()
{
return new Basis(
@@ -596,54 +625,54 @@ namespace Godot
private static readonly Vector3 _back = new Vector3(0, 0, 1);
/// <summary>
- /// Zero vector, a vector with all components set to `0`.
+ /// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
- /// <value>Equivalent to `new Vector3(0, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3(0, 0, 0)</c>.</value>
public static Vector3 Zero { get { return _zero; } }
/// <summary>
- /// One vector, a vector with all components set to `1`.
+ /// One vector, a vector with all components set to <c>1</c>.
/// </summary>
- /// <value>Equivalent to `new Vector3(1, 1, 1)`</value>
+ /// <value>Equivalent to <c>new Vector3(1, 1, 1)</c>.</value>
public static Vector3 One { get { return _one; } }
/// <summary>
- /// Infinity vector, a vector with all components set to `Mathf.Inf`.
+ /// Infinity vector, a vector with all components set to <see cref="Mathf.Inf"/>.
/// </summary>
- /// <value>Equivalent to `new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)`</value>
+ /// <value>Equivalent to <c>new Vector3(Mathf.Inf, Mathf.Inf, Mathf.Inf)</c>.</value>
public static Vector3 Inf { get { return _inf; } }
/// <summary>
/// Up unit vector.
/// </summary>
- /// <value>Equivalent to `new Vector3(0, 1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3(0, 1, 0)</c>.</value>
public static Vector3 Up { get { return _up; } }
/// <summary>
/// Down unit vector.
/// </summary>
- /// <value>Equivalent to `new Vector3(0, -1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3(0, -1, 0)</c>.</value>
public static Vector3 Down { get { return _down; } }
/// <summary>
/// Right unit vector. Represents the local direction of right,
/// and the global direction of east.
/// </summary>
- /// <value>Equivalent to `new Vector3(1, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3(1, 0, 0)</c>.</value>
public static Vector3 Right { get { return _right; } }
/// <summary>
/// Left unit vector. Represents the local direction of left,
/// and the global direction of west.
/// </summary>
- /// <value>Equivalent to `new Vector3(-1, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3(-1, 0, 0)</c>.</value>
public static Vector3 Left { get { return _left; } }
/// <summary>
/// Forward unit vector. Represents the local direction of forward,
/// and the global direction of north.
/// </summary>
- /// <value>Equivalent to `new Vector3(0, 0, -1)`</value>
+ /// <value>Equivalent to <c>new Vector3(0, 0, -1)</c>.</value>
public static Vector3 Forward { get { return _forward; } }
/// <summary>
/// Back unit vector. Represents the local direction of back,
/// and the global direction of south.
/// </summary>
- /// <value>Equivalent to `new Vector3(0, 0, 1)`</value>
+ /// <value>Equivalent to <c>new Vector3(0, 0, 1)</c>.</value>
public static Vector3 Back { get { return _back; } }
/// <summary>
@@ -670,6 +699,13 @@ namespace Godot
z = v.z;
}
+ /// <summary>
+ /// Adds each component of the <see cref="Vector3"/>
+ /// with the components of the given <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
public static Vector3 operator +(Vector3 left, Vector3 right)
{
left.x += right.x;
@@ -678,6 +714,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector3"/>
+ /// by the components of the given <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
public static Vector3 operator -(Vector3 left, Vector3 right)
{
left.x -= right.x;
@@ -686,6 +729,15 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector3"/>.
+ /// This is the same as writing <c>new Vector3(-v.x, -v.y, -v.z)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// With floats, the number zero can be either positive or negative.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
public static Vector3 operator -(Vector3 vec)
{
vec.x = -vec.x;
@@ -694,6 +746,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3 operator *(Vector3 vec, real_t scale)
{
vec.x *= scale;
@@ -702,6 +761,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3 operator *(real_t scale, Vector3 vec)
{
vec.x *= scale;
@@ -710,6 +776,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3"/>
+ /// by the components of the given <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3 operator *(Vector3 left, Vector3 right)
{
left.x *= right.x;
@@ -718,6 +791,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Vector3"/>
+ /// by the given <see cref="real_t"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
public static Vector3 operator /(Vector3 vec, real_t divisor)
{
vec.x /= divisor;
@@ -726,6 +806,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Vector3"/>
+ /// by the components of the given <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
public static Vector3 operator /(Vector3 vec, Vector3 divisorv)
{
vec.x /= divisorv.x;
@@ -734,6 +821,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector3"/>
+ /// with the components of the given <see cref="real_t"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(real_t)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector3(10, -20, 30) % 7); // Prints "(3, -6, 2)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector3 operator %(Vector3 vec, real_t divisor)
{
vec.x %= divisor;
@@ -742,6 +845,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector3"/>
+ /// with the components of the given <see cref="Vector3"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(Vector3)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector3(10, -20, 30) % new Vector3(7, 8, 9)); // Prints "(3, -4, 3)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector3 operator %(Vector3 vec, Vector3 divisorv)
{
vec.x %= divisorv.x;
@@ -750,16 +869,43 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
public static bool operator ==(Vector3 left, Vector3 right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector3 left, Vector3 right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Compares two <see cref="Vector3"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector3 left, Vector3 right)
{
if (left.x == right.x)
@@ -773,6 +919,17 @@ namespace Godot
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector3 left, Vector3 right)
{
if (left.x == right.x)
@@ -786,6 +943,17 @@ namespace Godot
return left.x > right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector3 left, Vector3 right)
{
if (left.x == right.x)
@@ -799,6 +967,17 @@ namespace Godot
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector3 left, Vector3 right)
{
if (left.x == right.x)
@@ -812,6 +991,14 @@ namespace Godot
return left.x > right.x;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is exactly equal
+ /// to the given object (<see paramref="obj"/>).
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector3)
@@ -822,14 +1009,21 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are exactly equal.
+ /// Note: Due to floating-point precision errors, consider using
+ /// <see cref="IsEqualApprox"/> instead, which is more reliable.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are exactly equal.</returns>
public bool Equals(Vector3 other)
{
return x == other.x && y == other.y && z == other.z;
}
/// <summary>
- /// Returns true if this vector and `other` are approximately equal, by running
- /// <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
+ /// Returns <see langword="true"/> if this vector and <paramref name="other"/> are approximately equal,
+ /// by running <see cref="Mathf.IsEqualApprox(real_t, real_t)"/> on each component.
/// </summary>
/// <param name="other">The other vector to compare.</param>
/// <returns>Whether or not the vectors are approximately equal.</returns>
@@ -838,16 +1032,28 @@ namespace Godot
return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z);
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector3"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Vector3"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y}, {z})";
}
+ /// <summary>
+ /// Converts this <see cref="Vector3"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})";
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
index c96a7cf1b0..eb06d2b87e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -17,32 +17,50 @@ namespace Godot
{
/// <summary>
/// Enumerated index values for the axes.
- /// Returned by <see cref="MaxAxis"/> and <see cref="MinAxis"/>.
+ /// Returned by <see cref="MaxAxisIndex"/> and <see cref="MinAxisIndex"/>.
/// </summary>
public enum Axis
{
+ /// <summary>
+ /// The vector's X axis.
+ /// </summary>
X = 0,
+ /// <summary>
+ /// The vector's Y axis.
+ /// </summary>
Y,
+ /// <summary>
+ /// The vector's Z axis.
+ /// </summary>
Z
}
/// <summary>
- /// The vector's X component. Also accessible by using the index position `[0]`.
+ /// The vector's X component. Also accessible by using the index position <c>[0]</c>.
/// </summary>
public int x;
+
/// <summary>
- /// The vector's Y component. Also accessible by using the index position `[1]`.
+ /// The vector's Y component. Also accessible by using the index position <c>[1]</c>.
/// </summary>
public int y;
+
/// <summary>
- /// The vector's Z component. Also accessible by using the index position `[2]`.
+ /// The vector's Z component. Also accessible by using the index position <c>[2]</c>.
/// </summary>
public int z;
/// <summary>
- /// Access vector components using their index.
+ /// Access vector components using their <paramref name="index"/>.
/// </summary>
- /// <value>`[0]` is equivalent to `.x`, `[1]` is equivalent to `.y`, `[2]` is equivalent to `.z`.</value>
+ /// <exception cref="IndexOutOfRangeException">
+ /// Thrown when the given the <paramref name="index"/> is not 0, 1 or 2.
+ /// </exception>
+ /// <value>
+ /// <c>[0]</c> is equivalent to <see cref="x"/>,
+ /// <c>[1]</c> is equivalent to <see cref="y"/>,
+ /// <c>[2]</c> is equivalent to <see cref="z"/>.
+ /// </value>
public int this[int index]
{
get
@@ -89,7 +107,7 @@ namespace Godot
/// <summary>
/// Returns a new vector with all components clamped between the
- /// components of `min` and `max` using
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
/// <see cref="Mathf.Clamp(int, int, int)"/>.
/// </summary>
/// <param name="min">The vector with minimum allowed values.</param>
@@ -106,40 +124,42 @@ namespace Godot
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns the squared distance between this vector and <paramref name="to"/>.
/// This method runs faster than <see cref="DistanceTo"/>, so prefer it if
/// you need to compare vectors or need the squared distance for some formula.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The squared distance between the two vectors.</returns>
- public int DistanceSquaredTo(Vector3i b)
+ public int DistanceSquaredTo(Vector3i to)
{
- return (b - this).LengthSquared();
+ return (to - this).LengthSquared();
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="to"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <seealso cref="DistanceSquaredTo(Vector3i)"/>
+ /// <param name="to">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
- public real_t DistanceTo(Vector3i b)
+ public real_t DistanceTo(Vector3i to)
{
- return (b - this).Length();
+ return (to - this).Length();
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="with"/>.
/// </summary>
- /// <param name="b">The other vector to use.</param>
+ /// <param name="with">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
- public int Dot(Vector3i b)
+ public int Dot(Vector3i with)
{
- return x * b.x + y * b.y + z * b.z;
+ return x * with.x + y * with.y + z * with.z;
}
/// <summary>
/// Returns the length (magnitude) of this vector.
/// </summary>
+ /// <seealso cref="LengthSquared"/>
/// <returns>The length of this vector.</returns>
public real_t Length()
{
@@ -166,30 +186,33 @@ namespace Godot
}
/// <summary>
- /// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's highest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
- /// <returns>The index of the largest axis.</returns>
- public Axis MaxAxis()
+ /// <returns>The index of the highest axis.</returns>
+ public Axis MaxAxisIndex()
{
return x < y ? (y < z ? Axis.Z : Axis.Y) : (x < z ? Axis.Z : Axis.X);
}
/// <summary>
- /// Returns the axis of the vector's smallest value. See <see cref="Axis"/>.
+ /// Returns the axis of the vector's lowest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.Z"/>.
/// </summary>
- /// <returns>The index of the smallest axis.</returns>
- public Axis MinAxis()
+ /// <returns>The index of the lowest axis.</returns>
+ public Axis MinAxisIndex()
{
return x < y ? (x < z ? Axis.X : Axis.Z) : (y < z ? Axis.Y : Axis.Z);
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `mod`.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
+ /// and <paramref name="mod"/>.
/// </summary>
/// <param name="mod">A value representing the divisor of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `mod`.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="mod"/>.
+ /// </returns>
public Vector3i PosMod(int mod)
{
Vector3i v = this;
@@ -200,10 +223,13 @@ namespace Godot
}
/// <summary>
- /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components and `modv`'s components.
+ /// Returns a vector composed of the <see cref="Mathf.PosMod(int, int)"/> of this vector's components
+ /// and <paramref name="modv"/>'s components.
/// </summary>
/// <param name="modv">A vector representing the divisors of the operation.</param>
- /// <returns>A vector with each component <see cref="Mathf.PosMod(int, int)"/> by `modv`'s components.</returns>
+ /// <returns>
+ /// A vector with each component <see cref="Mathf.PosMod(int, int)"/> by <paramref name="modv"/>'s components.
+ /// </returns>
public Vector3i PosMod(Vector3i modv)
{
Vector3i v = this;
@@ -218,7 +244,7 @@ namespace Godot
/// on the signs of this vector's components, or zero if the component is zero,
/// by calling <see cref="Mathf.Sign(int)"/> on each component.
/// </summary>
- /// <returns>A vector with all components as either `1`, `-1`, or `0`.</returns>
+ /// <returns>A vector with all components as either <c>1</c>, <c>-1</c>, or <c>0</c>.</returns>
public Vector3i Sign()
{
Vector3i v = this;
@@ -240,49 +266,49 @@ namespace Godot
private static readonly Vector3i _back = new Vector3i(0, 0, 1);
/// <summary>
- /// Zero vector, a vector with all components set to `0`.
+ /// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
- /// <value>Equivalent to `new Vector3i(0, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3i(0, 0, 0)</c>.</value>
public static Vector3i Zero { get { return _zero; } }
/// <summary>
- /// One vector, a vector with all components set to `1`.
+ /// One vector, a vector with all components set to <c>1</c>.
/// </summary>
- /// <value>Equivalent to `new Vector3i(1, 1, 1)`</value>
+ /// <value>Equivalent to <c>new Vector3i(1, 1, 1)</c>.</value>
public static Vector3i One { get { return _one; } }
/// <summary>
/// Up unit vector.
/// </summary>
- /// <value>Equivalent to `new Vector3i(0, 1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3i(0, 1, 0)</c>.</value>
public static Vector3i Up { get { return _up; } }
/// <summary>
/// Down unit vector.
/// </summary>
- /// <value>Equivalent to `new Vector3i(0, -1, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3i(0, -1, 0)</c>.</value>
public static Vector3i Down { get { return _down; } }
/// <summary>
/// Right unit vector. Represents the local direction of right,
/// and the global direction of east.
/// </summary>
- /// <value>Equivalent to `new Vector3i(1, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3i(1, 0, 0)</c>.</value>
public static Vector3i Right { get { return _right; } }
/// <summary>
/// Left unit vector. Represents the local direction of left,
/// and the global direction of west.
/// </summary>
- /// <value>Equivalent to `new Vector3i(-1, 0, 0)`</value>
+ /// <value>Equivalent to <c>new Vector3i(-1, 0, 0)</c>.</value>
public static Vector3i Left { get { return _left; } }
/// <summary>
/// Forward unit vector. Represents the local direction of forward,
/// and the global direction of north.
/// </summary>
- /// <value>Equivalent to `new Vector3i(0, 0, -1)`</value>
+ /// <value>Equivalent to <c>new Vector3i(0, 0, -1)</c>.</value>
public static Vector3i Forward { get { return _forward; } }
/// <summary>
/// Back unit vector. Represents the local direction of back,
/// and the global direction of south.
/// </summary>
- /// <value>Equivalent to `new Vector3i(0, 0, 1)`</value>
+ /// <value>Equivalent to <c>new Vector3i(0, 0, 1)</c>.</value>
public static Vector3i Back { get { return _back; } }
/// <summary>
@@ -321,6 +347,13 @@ namespace Godot
this.z = Mathf.RoundToInt(v.z);
}
+ /// <summary>
+ /// Adds each component of the <see cref="Vector3i"/>
+ /// with the components of the given <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The added vector.</returns>
public static Vector3i operator +(Vector3i left, Vector3i right)
{
left.x += right.x;
@@ -329,6 +362,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Subtracts each component of the <see cref="Vector3i"/>
+ /// by the components of the given <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The subtracted vector.</returns>
public static Vector3i operator -(Vector3i left, Vector3i right)
{
left.x -= right.x;
@@ -337,6 +377,14 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Returns the negative value of the <see cref="Vector3i"/>.
+ /// This is the same as writing <c>new Vector3i(-v.x, -v.y, -v.z)</c>.
+ /// This operation flips the direction of the vector while
+ /// keeping the same magnitude.
+ /// </summary>
+ /// <param name="vec">The vector to negate/flip.</param>
+ /// <returns>The negated/flipped vector.</returns>
public static Vector3i operator -(Vector3i vec)
{
vec.x = -vec.x;
@@ -345,6 +393,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3i operator *(Vector3i vec, int scale)
{
vec.x *= scale;
@@ -353,6 +408,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="scale">The scale to multiply by.</param>
+ /// <param name="vec">The vector to multiply.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3i operator *(int scale, Vector3i vec)
{
vec.x *= scale;
@@ -361,6 +423,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3i"/>
+ /// by the components of the given <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>The multiplied vector.</returns>
public static Vector3i operator *(Vector3i left, Vector3i right)
{
left.x *= right.x;
@@ -369,6 +438,13 @@ namespace Godot
return left;
}
+ /// <summary>
+ /// Multiplies each component of the <see cref="Vector3i"/>
+ /// by the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The divided vector.</returns>
public static Vector3i operator /(Vector3i vec, int divisor)
{
vec.x /= divisor;
@@ -377,6 +453,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Divides each component of the <see cref="Vector3i"/>
+ /// by the components of the given <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The divided vector.</returns>
public static Vector3i operator /(Vector3i vec, Vector3i divisorv)
{
vec.x /= divisorv.x;
@@ -385,6 +468,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector3i"/>
+ /// with the components of the given <see langword="int"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(int)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector3i(10, -20, 30) % 7); // Prints "(3, -6, 2)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisor">The divisor value.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector3i operator %(Vector3i vec, int divisor)
{
vec.x %= divisor;
@@ -393,6 +492,22 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Gets the remainder of each component of the <see cref="Vector3i"/>
+ /// with the components of the given <see cref="Vector3i"/>.
+ /// This operation uses truncated division, which is often not desired
+ /// as it does not work well with negative numbers.
+ /// Consider using <see cref="PosMod(Vector3i)"/> instead
+ /// if you want to handle negative numbers.
+ /// </summary>
+ /// <example>
+ /// <code>
+ /// GD.Print(new Vector3i(10, -20, 30) % new Vector3i(7, 8, 9)); // Prints "(3, -4, 3)"
+ /// </code>
+ /// </example>
+ /// <param name="vec">The dividend vector.</param>
+ /// <param name="divisorv">The divisor vector.</param>
+ /// <returns>The remainder vector.</returns>
public static Vector3i operator %(Vector3i vec, Vector3i divisorv)
{
vec.x %= divisorv.x;
@@ -401,6 +516,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Performs a bitwise AND operation with this <see cref="Vector3i"/>
+ /// and the given <see langword="int"/>.
+ /// </summary>
+ /// <param name="vec">The vector to AND with.</param>
+ /// <param name="and">The integer to AND with.</param>
+ /// <returns>The result of the bitwise AND.</returns>
public static Vector3i operator &(Vector3i vec, int and)
{
vec.x &= and;
@@ -409,6 +531,13 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Performs a bitwise AND operation with this <see cref="Vector3i"/>
+ /// and the given <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="vec">The left vector to AND with.</param>
+ /// <param name="andv">The right vector to AND with.</param>
+ /// <returns>The result of the bitwise AND.</returns>
public static Vector3i operator &(Vector3i vec, Vector3i andv)
{
vec.x &= andv.x;
@@ -417,78 +546,148 @@ namespace Godot
return vec;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
public static bool operator ==(Vector3i left, Vector3i right)
{
return left.Equals(right);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are not equal.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the vectors are not equal.</returns>
public static bool operator !=(Vector3i left, Vector3i right)
{
return !left.Equals(right);
}
+ /// <summary>
+ /// Compares two <see cref="Vector3i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than the right.</returns>
public static bool operator <(Vector3i left, Vector3i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
+ {
return left.z < right.z;
- else
- return left.y < right.y;
+ }
+ return left.y < right.y;
}
-
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than the right.</returns>
public static bool operator >(Vector3i left, Vector3i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
+ {
return left.z > right.z;
- else
- return left.y > right.y;
+ }
+ return left.y > right.y;
}
-
return left.x > right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is less than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is less than or equal to the right.</returns>
public static bool operator <=(Vector3i left, Vector3i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
+ {
return left.z <= right.z;
- else
- return left.y < right.y;
+ }
+ return left.y < right.y;
}
-
return left.x < right.x;
}
+ /// <summary>
+ /// Compares two <see cref="Vector3i"/> vectors by first checking if
+ /// the X value of the <paramref name="left"/> vector is greater than
+ /// or equal to the X value of the <paramref name="right"/> vector.
+ /// If the X values are exactly equal, then it repeats this check
+ /// with the Y values of the two vectors, and then with the Z values.
+ /// This operator is useful for sorting vectors.
+ /// </summary>
+ /// <param name="left">The left vector.</param>
+ /// <param name="right">The right vector.</param>
+ /// <returns>Whether or not the left is greater than or equal to the right.</returns>
public static bool operator >=(Vector3i left, Vector3i right)
{
if (left.x == right.x)
{
if (left.y == right.y)
+ {
return left.z >= right.z;
- else
- return left.y > right.y;
+ }
+ return left.y > right.y;
}
-
return left.x > right.x;
}
+ /// <summary>
+ /// Converts this <see cref="Vector3i"/> to a <see cref="Vector3"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
public static implicit operator Vector3(Vector3i value)
{
return new Vector3(value.x, value.y, value.z);
}
+ /// <summary>
+ /// Converts a <see cref="Vector3"/> to a <see cref="Vector3i"/>.
+ /// </summary>
+ /// <param name="value">The vector to convert.</param>
public static explicit operator Vector3i(Vector3 value)
{
return new Vector3i(value);
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vector is equal
+ /// to the given object (<see paramref="obj"/>).
+ /// </summary>
+ /// <param name="obj">The object to compare with.</param>
+ /// <returns>Whether or not the vector and the object are equal.</returns>
public override bool Equals(object obj)
{
if (obj is Vector3i)
@@ -499,21 +698,38 @@ namespace Godot
return false;
}
+ /// <summary>
+ /// Returns <see langword="true"/> if the vectors are equal.
+ /// </summary>
+ /// <param name="other">The other vector.</param>
+ /// <returns>Whether or not the vectors are equal.</returns>
public bool Equals(Vector3i other)
{
return x == other.x && y == other.y && z == other.z;
}
+ /// <summary>
+ /// Serves as the hash function for <see cref="Vector3i"/>.
+ /// </summary>
+ /// <returns>A hash code for this vector.</returns>
public override int GetHashCode()
{
return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode();
}
+ /// <summary>
+ /// Converts this <see cref="Vector3i"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public override string ToString()
{
return $"({x}, {y}, {z})";
}
+ /// <summary>
+ /// Converts this <see cref="Vector3i"/> to a string with the given <paramref name="format"/>.
+ /// </summary>
+ /// <returns>A string representation of this vector.</returns>
public string ToString(string format)
{
return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})";
diff --git a/modules/gdnative/net/stream_peer_gdnative.cpp b/modules/mono/glue/callable_glue.cpp
index 72ab72323d..54b65fdb94 100644
--- a/modules/gdnative/net/stream_peer_gdnative.cpp
+++ b/modules/mono/glue/callable_glue.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* stream_peer_gdnative.cpp */
+/* callable_glue.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,50 +28,52 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "stream_peer_gdnative.h"
+#ifdef MONO_GLUE_ENABLED
-StreamPeerGDNative::StreamPeerGDNative() {
- interface = nullptr;
-}
+#include "../mono_gd/gd_mono_marshal.h"
+#include "arguments_vector.h"
-StreamPeerGDNative::~StreamPeerGDNative() {
-}
+MonoObject *godot_icall_Callable_Call(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
+ Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-void StreamPeerGDNative::set_native_stream_peer(const godot_net_stream_peer *p_interface) {
- interface = p_interface;
-}
+ int argc = mono_array_length(p_args);
-void StreamPeerGDNative::_bind_methods() {
-}
+ ArgumentsVector<Variant> arg_store(argc);
+ ArgumentsVector<const Variant *> args(argc);
-Error StreamPeerGDNative::put_data(const uint8_t *p_data, int p_bytes) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)(interface->put_data(interface->data, p_data, p_bytes));
-}
+ for (int i = 0; i < argc; i++) {
+ MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
+ arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
+ args.set(i, &arg_store.get(i));
+ }
-Error StreamPeerGDNative::put_partial_data(const uint8_t *p_data, int p_bytes, int &r_sent) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)(interface->put_partial_data(interface->data, p_data, p_bytes, &r_sent));
-}
+ Variant result;
+ Callable::CallError error;
+ callable.call(args.ptr(), argc, result, error);
-Error StreamPeerGDNative::get_data(uint8_t *p_buffer, int p_bytes) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)(interface->get_data(interface->data, p_buffer, p_bytes));
+ return GDMonoMarshal::variant_to_mono_object(result);
}
-Error StreamPeerGDNative::get_partial_data(uint8_t *p_buffer, int p_bytes, int &r_received) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)(interface->get_partial_data(interface->data, p_buffer, p_bytes, &r_received));
-}
+void godot_icall_Callable_CallDeferred(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
+ Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
-int StreamPeerGDNative::get_available_bytes() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_bytes(interface->data);
-}
+ int argc = mono_array_length(p_args);
+
+ ArgumentsVector<Variant> arg_store(argc);
+ ArgumentsVector<const Variant *> args(argc);
-extern "C" {
+ for (int i = 0; i < argc; i++) {
+ MonoObject *elem = mono_array_get(p_args, MonoObject *, i);
+ arg_store.set(i, GDMonoMarshal::mono_object_to_variant(elem));
+ args.set(i, &arg_store.get(i));
+ }
-void GDAPI godot_net_bind_stream_peer(godot_object *p_obj, const godot_net_stream_peer *p_interface) {
- ((StreamPeerGDNative *)p_obj)->set_native_stream_peer(p_interface);
+ callable.call_deferred(args.ptr(), argc);
}
+
+void godot_register_callable_icalls() {
+ GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_Call", godot_icall_Callable_Call);
+ GDMonoUtils::add_internal_call("Godot.Callable::godot_icall_Callable_CallDeferred", godot_icall_Callable_CallDeferred);
}
+
+#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/collections_glue.cpp b/modules/mono/glue/collections_glue.cpp
index 86976de244..e367ecb7d6 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -144,7 +144,7 @@ void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) {
MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
int idx = ptr->find(GDMonoMarshal::mono_object_to_variant(item));
if (idx >= 0) {
- ptr->remove(idx);
+ ptr->remove_at(idx);
return true;
}
return false;
@@ -155,7 +155,7 @@ void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
}
- ptr->remove(index);
+ ptr->remove_at(index);
}
int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) {
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index a2ff868f65..07ddf5d945 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -182,26 +182,30 @@ void godot_icall_GD_printt(MonoArray *p_what) {
print_line(str);
}
-float godot_icall_GD_randf() {
- return Math::randf();
+void godot_icall_GD_randomize() {
+ Math::randomize();
}
uint32_t godot_icall_GD_randi() {
return Math::rand();
}
-void godot_icall_GD_randomize() {
- Math::randomize();
+float godot_icall_GD_randf() {
+ return Math::randf();
}
-double godot_icall_GD_randf_range(double from, double to) {
+int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
return Math::random(from, to);
}
-int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
+double godot_icall_GD_randf_range(double from, double to) {
return Math::random(from, to);
}
+double godot_icall_GD_randfn(double mean, double deviation) {
+ return Math::randfn(mean, deviation);
+}
+
uint32_t godot_icall_GD_rand_seed(uint64_t seed, uint64_t *newSeed) {
uint32_t ret = Math::rand_from_seed(&seed);
*newSeed = seed;
@@ -300,11 +304,12 @@ void godot_register_gd_icalls() {
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);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize);
- GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi", godot_icall_GD_randi);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf", godot_icall_GD_randf);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randi_range", godot_icall_GD_randi_range);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randf_range", godot_icall_GD_randf_range);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randfn", godot_icall_GD_randfn);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_rand_seed", godot_icall_GD_rand_seed);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_seed", godot_icall_GD_seed);
GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str", godot_icall_GD_str);
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index eed3bd2167..074220bb9b 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -36,6 +36,7 @@ void godot_register_collections_icalls();
void godot_register_gd_icalls();
void godot_register_string_name_icalls();
void godot_register_nodepath_icalls();
+void godot_register_callable_icalls();
void godot_register_object_icalls();
void godot_register_rid_icalls();
void godot_register_string_icalls();
@@ -50,6 +51,7 @@ void godot_register_glue_header_icalls() {
godot_register_gd_icalls();
godot_register_string_name_icalls();
godot_register_nodepath_icalls();
+ godot_register_callable_icalls();
godot_register_object_icalls();
godot_register_rid_icalls();
godot_register_string_icalls();
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 375ad413c4..2b4cc7fcc3 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -122,7 +122,7 @@ public:
private:
_GodotSharpDirs() {
- res_data_dir = "res://.godot/mono";
+ res_data_dir = ProjectSettings::get_singleton()->get_project_data_path().plus_file("mono");
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
@@ -229,9 +229,6 @@ private:
#endif
}
- _GodotSharpDirs(const _GodotSharpDirs &);
- _GodotSharpDirs &operator=(const _GodotSharpDirs &);
-
public:
static _GodotSharpDirs &get_singleton() {
static _GodotSharpDirs singleton;
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index d0e51d159f..a18a4ce646 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -56,13 +56,12 @@ struct MonoGCHandleData {
void release();
- MonoGCHandleData &operator=(const MonoGCHandleData &p_other) {
+ void operator=(const MonoGCHandleData &p_other) {
#ifdef DEBUG_ENABLED
CRASH_COND(!is_released());
#endif
handle = p_other.handle;
type = p_other.type;
- return *this;
}
MonoGCHandleData(const MonoGCHandleData &) = default;
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 1b1349a3a3..52447bc59b 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -151,7 +151,7 @@ void gd_mono_debug_init() {
if (da_args.length() == 0) {
da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
- ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
+ ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
.utf8();
}
#else
@@ -504,7 +504,7 @@ void GDMono::_init_godot_api_hashes() {
}
void GDMono::_init_exception_policy() {
- PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/unhandled_exception_policy", PROPERTY_HINT_ENUM,
+ PropertyInfo exc_policy_prop = PropertyInfo(Variant::INT, "mono/runtime/unhandled_exception_policy", PROPERTY_HINT_ENUM,
vformat("Terminate Application:%s,Log Error:%s", (int)POLICY_TERMINATE_APP, (int)POLICY_LOG_ERROR));
unhandled_exception_policy = (UnhandledExceptionPolicy)(int)GLOBAL_DEF(exc_policy_prop.name, (int)POLICY_TERMINATE_APP);
ProjectSettings::get_singleton()->set_custom_property_info(exc_policy_prop.name, exc_policy_prop);
@@ -592,9 +592,9 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo
ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) {
ApiAssemblyInfo::Version api_assembly_version;
- const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE ?
- BINDINGS_CLASS_NATIVECALLS :
- BINDINGS_CLASS_NATIVECALLS_EDITOR;
+ const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE
+ ? BINDINGS_CLASS_NATIVECALLS
+ : BINDINGS_CLASS_NATIVECALLS_EDITOR;
GDMonoClass *nativecalls_klass = p_api_assembly->get_class(BINDINGS_NAMESPACE, nativecalls_name);
@@ -702,11 +702,11 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool
}
r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") ||
- GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") ||
- GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") ||
- GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") ||
- GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") ||
- GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash");
+ GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") ||
+ GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") ||
+ GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") ||
+ GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") ||
+ GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash");
return true;
}
@@ -754,14 +754,10 @@ bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config
}
String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {
-#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
- ( \
- (m_out_of_sync ? \
- String("The assembly is invalidated ") : \
- String("The assembly was not found ")) + \
- (m_prebuilt_exists ? \
- String("and the prebuilt assemblies are missing.") : \
- String("and we failed to copy the prebuilt assemblies.")))
+#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
+ ( \
+ (m_out_of_sync ? String("The assembly is invalidated ") : String("The assembly was not found ")) + \
+ (m_prebuilt_exists ? String("and the prebuilt assemblies are missing.") : String("and we failed to copy the prebuilt assemblies.")))
String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
@@ -819,14 +815,14 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
// If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Main::is_project_manager() ?
- GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
- GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
+ String assembly_dir = !Main::is_project_manager()
+ ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
+ : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
+ load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
#else
bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &r_loaded_api_assembly.assembly, p_refonly);
#endif
@@ -834,8 +830,8 @@ bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, c
if (success) {
ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE);
r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
} else {
r_loaded_api_assembly.out_of_sync = false;
}
@@ -852,20 +848,20 @@ bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly,
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
// If running the project manager, load it from the prebuilt API directory
- String assembly_dir = !Main::is_project_manager() ?
- GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
- GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
+ String assembly_dir = !Main::is_project_manager()
+ ? GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config)
+ : GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
+ load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
if (success) {
ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR);
r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
} else {
r_loaded_api_assembly.out_of_sync = false;
}
@@ -985,7 +981,7 @@ bool GDMono::_load_tools_assemblies() {
}
bool success = load_assembly(TOOLS_ASM_NAME, &tools_assembly) &&
- load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
+ load_assembly(TOOLS_PROJECT_EDITOR_ASM_NAME, &tools_project_editor_assembly);
return success;
}
@@ -1363,8 +1359,8 @@ int32_t GodotSharp::get_scripts_domain_id() {
bool GodotSharp::is_scripts_domain_loaded() {
return GDMono::get_singleton() != nullptr &&
- GDMono::get_singleton()->is_runtime_initialized() &&
- GDMono::get_singleton()->get_scripts_domain() != nullptr;
+ GDMono::get_singleton()->is_runtime_initialized() &&
+ GDMono::get_singleton()->get_scripts_domain() != nullptr;
}
bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 4170e5053f..a18fa6c6b4 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -54,8 +54,8 @@ struct Version {
bool operator==(const Version &p_other) const {
return godot_api_hash == p_other.godot_api_hash &&
- bindings_version == p_other.bindings_version &&
- cs_glue_version == p_other.cs_glue_version;
+ bindings_version == p_other.bindings_version &&
+ cs_glue_version == p_other.cs_glue_version;
}
Version() {}
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 8b215a66c2..60277e0652 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -140,8 +140,8 @@ void CachedData::clear_godot_api_cache() {
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
- class_RemoteAttribute = nullptr;
- class_PuppetAttribute = nullptr;
+ class_AnyPeerAttribute = nullptr;
+ class_AuthorityAttribute = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
class_ScriptPathAttribute = nullptr;
@@ -177,6 +177,8 @@ void CachedData::clear_godot_api_cache() {
methodthunk_MarshalUtils_TypeIsGenericICollection.nullify();
methodthunk_MarshalUtils_TypeIsGenericIDictionary.nullify();
+ methodthunk_MarshalUtils_GetGenericTypeDefinition.nullify();
+
methodthunk_MarshalUtils_ArrayGetElementType.nullify();
methodthunk_MarshalUtils_DictionaryGetKeyValueTypes.nullify();
@@ -265,8 +267,8 @@ 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(RemoteAttribute, GODOT_API_CLASS(RemoteAttribute));
- CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
+ CACHE_CLASS_AND_CHECK(AnyPeerAttribute, GODOT_API_CLASS(AnyPeerAttribute));
+ CACHE_CLASS_AND_CHECK(AuthorityAttribute, GODOT_API_CLASS(AuthorityAttribute));
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));
@@ -299,6 +301,8 @@ void update_godot_api_cache() {
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, GetGenericTypeDefinition, GODOT_API_CLASS(MarshalUtils)->get_method("GetGenericTypeDefinition", 2));
+
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, ArrayGetElementType, GODOT_API_CLASS(MarshalUtils)->get_method("ArrayGetElementType", 2));
CACHE_METHOD_THUNK_AND_CHECK(MarshalUtils, DictionaryGetKeyValueTypes, GODOT_API_CLASS(MarshalUtils)->get_method("DictionaryGetKeyValueTypes", 3));
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index fd28bbda14..5101907bd6 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -111,8 +111,8 @@ struct CachedData {
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
- GDMonoClass *class_RemoteAttribute;
- GDMonoClass *class_PuppetAttribute;
+ GDMonoClass *class_AnyPeerAttribute;
+ GDMonoClass *class_AuthorityAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
GDMonoClass *class_ScriptPathAttribute;
@@ -148,6 +148,8 @@ struct CachedData {
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericICollection;
GDMonoMethodThunkR<MonoBoolean, MonoReflectionType *> methodthunk_MarshalUtils_TypeIsGenericIDictionary;
+ GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_GetGenericTypeDefinition;
+
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **> methodthunk_MarshalUtils_ArrayGetElementType;
GDMonoMethodThunk<MonoReflectionType *, MonoReflectionType **, MonoReflectionType **> methodthunk_MarshalUtils_DictionaryGetKeyValueTypes;
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 27b4ac7fa7..520568071e 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -187,7 +187,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
#ifdef DEBUG_ENABLED
String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")";
WARN_PRINT("Method '" + fullname + "' is hidden by Godot API method. Should be '" +
- method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'.");
+ method->get_full_name_no_class() + "'. In class '" + namespace_name + "." + class_name + "'.");
#endif
continue;
}
@@ -205,7 +205,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
// found
String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")";
WARN_PRINT("Method '" + fullname + "' should be '" + m->get_full_name_no_class() +
- "'. In class '" + namespace_name + "." + class_name + "'.");
+ "'. In class '" + namespace_name + "." + class_name + "'.");
break;
}
@@ -464,9 +464,18 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
return delegates_list;
}
+ // If the class is generic we must use the generic type definition.
+ MonoClass *klass = mono_class;
+ if (mono_type_get_type(get_mono_type()) == MONO_TYPE_GENERICINST) {
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), get_mono_type());
+ GDMonoUtils::Marshal::get_generic_type_definition(reftype, &reftype);
+ MonoType *type = mono_reflection_type_get_type(reftype);
+ klass = mono_class_from_mono_type(type);
+ }
+
void *iter = nullptr;
MonoClass *raw_class = nullptr;
- while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) {
+ while ((raw_class = mono_class_get_nested_types(klass, &iter)) != nullptr) {
if (mono_class_is_delegate(raw_class)) {
StringName name = String::utf8(mono_class_get_name(raw_class));
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index 179bbfb40c..bcdcd6623b 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -121,12 +121,10 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
ERR_FAIL_COND(da->list_dir_begin() != OK);
- String current;
- while ((current = da->get_next()).length()) {
- if (da->current_is_dir()) {
- continue;
- }
- if (!current.ends_with(".txt")) {
+ String current = da->get_next();
+ while (!current.is_empty()) {
+ if (da->current_is_dir() || !current.ends_with(".txt")) {
+ current = da->get_next();
continue;
}
@@ -135,6 +133,7 @@ void GDMonoLog::_delete_old_log_files(const String &p_logs_dir) {
if (OS::get_singleton()->get_unix_time() - modified_time > MAX_SECS) {
da->remove(current);
}
+ current = da->get_next();
}
da->list_dir_end();
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c9789bf270..6b395303dd 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -139,49 +139,65 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
+ MonoClass *elem_class = mono_class_get_element_class(p_type.type_class->get_mono_ptr());
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
+ if (elem_class == CACHED_CLASS_RAW(MonoObject)) {
return Variant::ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
+ if (elem_class == CACHED_CLASS_RAW(uint8_t)) {
return Variant::PACKED_BYTE_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
+ if (elem_class == CACHED_CLASS_RAW(int32_t)) {
return Variant::PACKED_INT32_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
+ if (elem_class == CACHED_CLASS_RAW(int64_t)) {
return Variant::PACKED_INT64_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
+ if (elem_class == CACHED_CLASS_RAW(float)) {
return Variant::PACKED_FLOAT32_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
+ if (elem_class == CACHED_CLASS_RAW(double)) {
return Variant::PACKED_FLOAT64_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
+ if (elem_class == CACHED_CLASS_RAW(String)) {
return Variant::PACKED_STRING_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
+ if (elem_class == CACHED_CLASS_RAW(Vector2)) {
return Variant::PACKED_VECTOR2_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
+ if (elem_class == CACHED_CLASS_RAW(Vector3)) {
return Variant::PACKED_VECTOR3_ARRAY;
}
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
+ if (elem_class == CACHED_CLASS_RAW(Color)) {
return Variant::PACKED_COLOR_ARRAY;
}
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+ if (elem_class == CACHED_CLASS_RAW(StringName)) {
+ return Variant::ARRAY;
+ }
+
+ if (elem_class == CACHED_CLASS_RAW(NodePath)) {
+ return Variant::ARRAY;
+ }
+
+ if (elem_class == CACHED_CLASS_RAW(RID)) {
+ return Variant::ARRAY;
+ }
+
+ if (mono_class_is_enum(elem_class)) {
+ return Variant::ARRAY;
+ }
+
+ GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(elem_class);
if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
return Variant::ARRAY;
}
@@ -266,6 +282,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
return Variant::ARRAY;
}
+
+ // GodotObject
+ GDMonoClass *type_class = p_type.type_class;
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ return Variant::OBJECT;
+ }
} break;
default: {
@@ -284,9 +306,8 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
switch (p_array_type.type_encoding) {
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type());
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- r_elem_type = ManagedType::from_class(array_type_class);
+ MonoClass *elem_class = mono_class_get_element_class(p_array_type.type_class->get_mono_ptr());
+ r_elem_type = ManagedType::from_class(elem_class);
return true;
} break;
case MONO_TYPE_GENERICINST: {
@@ -361,12 +382,23 @@ MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class
return PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
}
+ if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
+ return Array_to_mono_array(p_var.operator Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
+ return Array_to_mono_array(p_var.operator Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
+ return Array_to_mono_array(p_var.operator Array());
+ }
+
if (mono_class_is_assignable_from(CACHED_CLASS(GodotObject)->get_mono_ptr(), array_type->eklass)) {
return Array_to_mono_array(p_var.operator ::Array(), array_type->eklass);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" +
- GDMonoClass::get_full_name(array_type->eklass) + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to array of unsupported element type:" + GDMonoClass::get_full_name(array_type->eklass) + "'.");
}
MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) {
@@ -399,8 +431,7 @@ MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p
return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" +
- p_type_class->get_full_name() + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type: '" + p_type_class->get_full_name() + "'.");
}
MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) {
@@ -450,8 +481,12 @@ MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoCl
return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" +
- p_type_class->get_full_name() + "'.");
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) {
+ return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported generic type: '" + p_type_class->get_full_name() + "'.");
}
MonoObject *variant_to_mono_object(const Variant &p_var) {
@@ -786,14 +821,12 @@ void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type
RETURN_TYPE_VAL(uint64_t, val);
}
default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" +
- GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(mono_class_from_mono_type(enum_basetype)) + "'.");
}
}
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" +
- p_type.type_class->get_full_name() + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
} break;
#undef RETURN_TYPE_VAL
case MONO_TYPE_STRING:
@@ -809,8 +842,7 @@ void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type
return variant_to_mono_object(p_var);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " +
- itos(p_type.type_encoding) + ".");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
}
MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
@@ -943,14 +975,12 @@ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_ty
return BOX_ENUM(enum_baseclass, val);
}
default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" +
- GDMonoClass::get_full_name(enum_baseclass) + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'.");
}
}
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" +
- p_type.type_class->get_full_name() + "'.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
} break;
case MONO_TYPE_STRING:
return (MonoObject *)variant_to_mono_string(p_var);
@@ -965,8 +995,7 @@ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_ty
return variant_to_mono_object(p_var);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " +
- itos(p_type.type_encoding) + ".");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to unsupported type with encoding: " + itos(p_type.type_encoding) + ".");
}
Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type, bool p_fail_with_err = true) {
@@ -1118,6 +1147,18 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return mono_array_to_PackedColorArray((MonoArray *)p_obj);
}
+ if (array_type->eklass == CACHED_CLASS_RAW(StringName)) {
+ return mono_array_to_Array((MonoArray *)p_obj);
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(NodePath)) {
+ return mono_array_to_Array((MonoArray *)p_obj);
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(RID)) {
+ return mono_array_to_Array((MonoArray *)p_obj);
+ }
+
GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
return mono_array_to_Array((MonoArray *)p_obj);
@@ -1206,12 +1247,22 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
return system_generic_list_to_Array_variant(p_obj, p_type.type_class, elem_reftype);
}
+
+ // GodotObject
+ GDMonoClass *type_class = p_type.type_class;
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
+ Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
+ if (ptr != nullptr) {
+ RefCounted *rc = Object::cast_to<RefCounted>(ptr);
+ return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
+ }
+ return Variant();
+ }
} break;
}
if (p_fail_with_err) {
- ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" +
- p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
+ ERR_FAIL_V_MSG(Variant(), "Attempted to convert an unmarshallable managed type to Variant. Name: '" + p_type.type_class->get_name() + "' Encoding: " + itos(p_type.type_encoding) + ".");
} else {
return Variant();
}
@@ -1271,7 +1322,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
+ ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(ctor == nullptr);
@@ -1293,7 +1344,7 @@ MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoCl
Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]] GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(p_key_reftype, p_value_reftype);
String ctor_desc = ":.ctor(System.Collections.Generic.IDictionary`2<" + GDMonoUtils::get_type_desc(p_key_reftype) +
- ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
+ ", " + GDMonoUtils::get_type_desc(p_value_reftype) + ">)";
GDMonoMethod *godot_dict_ctor = godot_dict_class->get_method_with_desc(ctor_desc, true);
CRASH_COND(godot_dict_ctor == nullptr);
@@ -1315,7 +1366,6 @@ Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, [[maybe_unused]]
MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype) {
MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype);
- MonoClass *elem_class = mono_class_from_mono_type(elem_type);
String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + GDMonoUtils::get_type_desc(elem_type) + ">)";
GDMonoMethod *ctor = p_class->get_method_with_desc(ctor_desc, true);
@@ -1324,7 +1374,10 @@ MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_cl
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
ERR_FAIL_NULL_V(mono_object, nullptr);
- void *ctor_args[1] = { Array_to_mono_array(p_array, elem_class) };
+ GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(p_elem_reftype);
+ MonoObject *godot_array = GDMonoUtils::create_managed_from(p_array, godot_array_class);
+
+ void *ctor_args[1] = { godot_array };
MonoException *exc = nullptr;
ctor->invoke_raw(mono_object, ctor_args, &exc);
@@ -1683,12 +1736,12 @@ Callable managed_to_callable(const M_Callable &p_managed_callable) {
CallableCustom *managed_callable = memnew(ManagedCallable(p_managed_callable.delegate));
return Callable(managed_callable);
} else {
- Object *target = p_managed_callable.target ?
- unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target)) :
- nullptr;
- StringName *method_ptr = p_managed_callable.method_string_name ?
- unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name)) :
- nullptr;
+ Object *target = p_managed_callable.target
+ ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_callable.target))
+ : nullptr;
+ StringName *method_ptr = p_managed_callable.method_string_name
+ ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name))
+ : nullptr;
StringName method = method_ptr ? *method_ptr : StringName();
return Callable(target, method);
}
@@ -1731,12 +1784,12 @@ M_Callable callable_to_managed(const Callable &p_callable) {
}
Signal managed_to_signal_info(const M_SignalInfo &p_managed_signal) {
- Object *owner = p_managed_signal.owner ?
- unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner)) :
- nullptr;
- StringName *name_ptr = p_managed_signal.name_string_name ?
- unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name)) :
- nullptr;
+ Object *owner = p_managed_signal.owner
+ ? unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_managed_signal.owner))
+ : nullptr;
+ StringName *name_ptr = p_managed_signal.name_string_name
+ ? unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name))
+ : nullptr;
StringName name = name_ptr ? *name_ptr : StringName();
return Signal(owner, name);
}
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index 88afc7ebc5..2f4b619b61 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -234,58 +234,58 @@ enum {
#endif
MATCHES_Vector2 = (MATCHES_real_t && (sizeof(Vector2) == (sizeof(real_t) * 2)) &&
- offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector2, y) == (sizeof(real_t) * 1)),
+ offsetof(Vector2, x) == (sizeof(real_t) * 0) &&
+ offsetof(Vector2, y) == (sizeof(real_t) * 1)),
MATCHES_Vector2i = (MATCHES_int && (sizeof(Vector2i) == (sizeof(int32_t) * 2)) &&
- offsetof(Vector2i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector2i, y) == (sizeof(int32_t) * 1)),
+ offsetof(Vector2i, x) == (sizeof(int32_t) * 0) &&
+ offsetof(Vector2i, y) == (sizeof(int32_t) * 1)),
MATCHES_Rect2 = (MATCHES_Vector2 && (sizeof(Rect2) == (sizeof(Vector2) * 2)) &&
- offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
- offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
+ offsetof(Rect2, position) == (sizeof(Vector2) * 0) &&
+ offsetof(Rect2, size) == (sizeof(Vector2) * 1)),
MATCHES_Rect2i = (MATCHES_Vector2i && (sizeof(Rect2i) == (sizeof(Vector2i) * 2)) &&
- offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) &&
- offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)),
+ offsetof(Rect2i, position) == (sizeof(Vector2i) * 0) &&
+ offsetof(Rect2i, size) == (sizeof(Vector2i) * 1)),
MATCHES_Transform2D = (MATCHES_Vector2 && (sizeof(Transform2D) == (sizeof(Vector2) * 3))), // No field offset required, it stores an array
MATCHES_Vector3 = (MATCHES_real_t && (sizeof(Vector3) == (sizeof(real_t) * 3)) &&
- offsetof(Vector3, x) == (sizeof(real_t) * 0) &&
- offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
- offsetof(Vector3, z) == (sizeof(real_t) * 2)),
+ offsetof(Vector3, x) == (sizeof(real_t) * 0) &&
+ offsetof(Vector3, y) == (sizeof(real_t) * 1) &&
+ offsetof(Vector3, z) == (sizeof(real_t) * 2)),
MATCHES_Vector3i = (MATCHES_int && (sizeof(Vector3i) == (sizeof(int32_t) * 3)) &&
- offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
- offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
- offsetof(Vector3i, z) == (sizeof(int32_t) * 2)),
+ offsetof(Vector3i, x) == (sizeof(int32_t) * 0) &&
+ offsetof(Vector3i, y) == (sizeof(int32_t) * 1) &&
+ offsetof(Vector3i, z) == (sizeof(int32_t) * 2)),
MATCHES_Basis = (MATCHES_Vector3 && (sizeof(Basis) == (sizeof(Vector3) * 3))), // No field offset required, it stores an array
MATCHES_Quaternion = (MATCHES_real_t && (sizeof(Quaternion) == (sizeof(real_t) * 4)) &&
- offsetof(Quaternion, x) == (sizeof(real_t) * 0) &&
- offsetof(Quaternion, y) == (sizeof(real_t) * 1) &&
- offsetof(Quaternion, z) == (sizeof(real_t) * 2) &&
- offsetof(Quaternion, w) == (sizeof(real_t) * 3)),
+ offsetof(Quaternion, x) == (sizeof(real_t) * 0) &&
+ offsetof(Quaternion, y) == (sizeof(real_t) * 1) &&
+ offsetof(Quaternion, z) == (sizeof(real_t) * 2) &&
+ offsetof(Quaternion, w) == (sizeof(real_t) * 3)),
MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) &&
- offsetof(Transform3D, basis) == 0 &&
- offsetof(Transform3D, origin) == sizeof(Basis)),
+ offsetof(Transform3D, basis) == 0 &&
+ offsetof(Transform3D, origin) == sizeof(Basis)),
MATCHES_AABB = (MATCHES_Vector3 && (sizeof(AABB) == (sizeof(Vector3) * 2)) &&
- offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
- offsetof(AABB, size) == (sizeof(Vector3) * 1)),
+ offsetof(AABB, position) == (sizeof(Vector3) * 0) &&
+ offsetof(AABB, size) == (sizeof(Vector3) * 1)),
MATCHES_Color = (MATCHES_float && (sizeof(Color) == (sizeof(float) * 4)) &&
- offsetof(Color, r) == (sizeof(float) * 0) &&
- offsetof(Color, g) == (sizeof(float) * 1) &&
- offsetof(Color, b) == (sizeof(float) * 2) &&
- offsetof(Color, a) == (sizeof(float) * 3)),
+ offsetof(Color, r) == (sizeof(float) * 0) &&
+ offsetof(Color, g) == (sizeof(float) * 1) &&
+ offsetof(Color, b) == (sizeof(float) * 2) &&
+ offsetof(Color, a) == (sizeof(float) * 3)),
MATCHES_Plane = (MATCHES_Vector3 && MATCHES_real_t && (sizeof(Plane) == (sizeof(Vector3) + sizeof(real_t))) &&
- offsetof(Plane, normal) == 0 &&
- offsetof(Plane, d) == sizeof(Vector3))
+ offsetof(Plane, normal) == 0 &&
+ offsetof(Plane, d) == sizeof(Vector3))
};
// In the future we may force this if we want to ref return these structs
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index 5391b7775e..5c7cf29e88 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -65,6 +65,8 @@ GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_own
type.type_class = GDMono::get_singleton()->get_class(param_type_class);
}
+ param_buffer_size = GDMonoMarshal::variant_get_managed_unboxed_size(type);
+
attrs_fetched = false;
attributes = nullptr;
}
@@ -147,24 +149,20 @@ bool GDMonoProperty::has_setter() {
return mono_property_get_set_method(mono_property) != nullptr;
}
-void GDMonoProperty::set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc) {
- MonoMethod *prop_method = mono_property_get_set_method(mono_property);
- void *params[1] = { p_value };
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_invoke(prop_method, p_object, params, &exc);
- if (exc) {
- if (r_exc) {
- *r_exc = exc;
- } else {
- GDMonoUtils::set_pending_exception(exc);
- }
- }
-}
+void GDMonoProperty::set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc) {
+ uint8_t *buffer = (uint8_t *)alloca(param_buffer_size);
+ unsigned int offset = 0;
-void GDMonoProperty::set_value(MonoObject *p_object, void **p_params, MonoException **r_exc) {
- MonoException *exc = nullptr;
- GDMonoUtils::property_set_value(mono_property, p_object, p_params, &exc);
+ void *params[1] = {
+ GDMonoMarshal::variant_to_managed_unboxed(p_value, type, buffer, offset)
+ };
+
+#ifdef DEBUG_ENABLED
+ CRASH_COND(offset != param_buffer_size);
+#endif
+ MonoException *exc = nullptr;
+ GDMonoUtils::property_set_value(mono_property, p_object, params, &exc);
if (exc) {
if (r_exc) {
*r_exc = exc;
diff --git a/modules/mono/mono_gd/gd_mono_property.h b/modules/mono/mono_gd/gd_mono_property.h
index af7a2c02e5..9bb1caa759 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -45,6 +45,8 @@ class GDMonoProperty : public IMonoClassMember {
bool attrs_fetched;
MonoCustomAttrInfo *attributes;
+ unsigned int param_buffer_size;
+
public:
virtual GDMonoClass *get_enclosing_class() const final { return owner; }
@@ -64,8 +66,7 @@ public:
_FORCE_INLINE_ ManagedType get_type() const { return type; }
- void set_value(MonoObject *p_object, MonoObject *p_value, MonoException **r_exc = nullptr);
- void set_value(MonoObject *p_object, void **p_params, MonoException **r_exc = nullptr);
+ void set_value_from_variant(MonoObject *p_object, const Variant &p_value, MonoException **r_exc = nullptr);
MonoObject *get_value(MonoObject *p_object, MonoException **r_exc = nullptr);
bool get_bool_value(MonoObject *p_object);
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index 13939bd014..505c637af9 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -450,7 +450,7 @@ void debug_send_unhandled_exception_error(MonoException *p_exc) {
int line = si.size() ? si[0].line : __LINE__;
String error_msg = "Unhandled exception";
- EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
+ EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si);
#endif
}
@@ -614,6 +614,12 @@ bool type_is_generic_idictionary(MonoReflectionType *p_reftype) {
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);
+ UNHANDLED_EXCEPTION(exc);
+}
+
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
MonoException *exc = nullptr;
CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType).invoke(p_array_reftype, r_elem_reftype, &exc);
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 773501e93d..3162ef198d 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -62,6 +62,8 @@ bool type_is_generic_ienumerable(MonoReflectionType *p_reftype);
bool type_is_generic_icollection(MonoReflectionType *p_reftype);
bool type_is_generic_idictionary(MonoReflectionType *p_reftype);
+void get_generic_type_definition(MonoReflectionType *p_reftype, MonoReflectionType **r_generic_reftype);
+
void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
index 366662ff81..c49a62a632 100644
--- a/modules/mono/mono_gd/gd_mono_wasm_m2n.h
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
@@ -158,7 +158,7 @@ T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
return (T)(size_t)p_margs->iargs[p_idx];
} else if constexpr (cookie == 'L') {
static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> ||
- (sizeof(void *) == 8 && std::is_pointer_v<T>),
+ (sizeof(void *) == 8 && std::is_pointer_v<T>),
"Invalid type for cookie 'L'.");
union {
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 6b616dd52d..d0a27b27c1 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -35,7 +35,7 @@
#include "core/os/os.h"
-// Here, after os/os.h
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace MonoRegUtils {
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index ec04d50704..64aec5d359 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -36,6 +36,7 @@
#include "core/os/os.h"
#ifdef WINDOWS_ENABLED
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define ENV_PATH_SEP ";"
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 053618ebe4..74f5e6d18a 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -139,24 +139,24 @@ bool is_csharp_keyword(const String &p_name) {
// Reserved keywords
return p_name == "abstract" || p_name == "as" || p_name == "base" || p_name == "bool" ||
- p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
- p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
- p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
- 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 == "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" ||
- p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
- p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
- p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
- p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
- p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
- p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
- p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
- p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
+ p_name == "break" || p_name == "byte" || p_name == "case" || p_name == "catch" ||
+ p_name == "char" || p_name == "checked" || p_name == "class" || p_name == "const" ||
+ p_name == "continue" || p_name == "decimal" || p_name == "default" || p_name == "delegate" ||
+ 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 == "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" ||
+ p_name == "out" || p_name == "override" || p_name == "params" || p_name == "private" ||
+ p_name == "protected" || p_name == "public" || p_name == "readonly" || p_name == "ref" ||
+ p_name == "return" || p_name == "sbyte" || p_name == "sealed" || p_name == "short" ||
+ p_name == "sizeof" || p_name == "stackalloc" || p_name == "static" || p_name == "string" ||
+ p_name == "struct" || p_name == "switch" || p_name == "this" || p_name == "throw" ||
+ p_name == "true" || p_name == "try" || p_name == "typeof" || p_name == "uint" || p_name == "ulong" ||
+ p_name == "unchecked" || p_name == "unsafe" || p_name == "ushort" || p_name == "using" ||
+ p_name == "virtual" || p_name == "volatile" || p_name == "void" || p_name == "while";
}
String escape_csharp_keyword(const String &p_name) {
@@ -200,12 +200,12 @@ String str_format(const char *p_format, ...) {
return res;
}
-#if defined(MINGW_ENABLED) || defined(_MSC_VER) && _MSC_VER < 1900
-#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_args_copy)
-#define gd_vscprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy)
+#if defined(MINGW_ENABLED)
+#define RSnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf_s(m_buffer, m_count, _TRUNCATE, m_format, m_args_copy)
+#define RScprintf(m_format, m_args_copy) _vscprintf(m_format, m_args_copy)
#else
-#define gd_vsnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy)
-#define gd_vscprintf(m_format, m_args_copy) vsnprintf(nullptr, 0, p_format, m_args_copy)
+#define RSnprintf(m_buffer, m_count, m_format, m_args_copy) vsnprintf(m_buffer, m_count, m_format, m_args_copy)
+#define RScprintf(m_format, m_args_copy) vsnprintf(nullptr, 0, p_format, m_args_copy)
#endif
String str_format(const char *p_format, va_list p_list) {
@@ -231,7 +231,7 @@ char *str_format_new(const char *p_format, va_list p_list) {
va_list list;
va_copy(list, p_list);
- int len = gd_vscprintf(p_format, list);
+ int len = RScprintf(p_format, list);
va_end(list);
len += 1; // for the trailing '/0'
@@ -239,7 +239,7 @@ char *str_format_new(const char *p_format, va_list p_list) {
char *buffer(memnew_arr(char, len));
va_copy(list, p_list);
- gd_vsnprintf(buffer, len, p_format, list);
+ RSnprintf(buffer, len, p_format, list);
va_end(list);
return buffer;
diff --git a/modules/navigation/godot_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index fd965674d6..ac3422187f 100644
--- a/modules/navigation/godot_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -133,13 +133,13 @@ RID GodotNavigationServer::map_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
RID rid = map_owner.make_rid();
- NavMap *space = map_owner.getornull(rid);
+ NavMap *space = map_owner.get_or_null(rid);
space->set_self(rid);
return rid;
}
COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
if (p_active) {
@@ -150,90 +150,90 @@ COMMAND_2(map_set_active, RID, p_map, bool, p_active) {
} else {
int map_index = active_maps.find(map);
ERR_FAIL_COND(map_index < 0);
- active_maps.remove(map_index);
- active_maps_update_id.remove(map_index);
+ active_maps.remove_at(map_index);
+ active_maps_update_id.remove_at(map_index);
}
}
bool GodotNavigationServer::map_is_active(RID p_map) const {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, false);
return active_maps.find(map) >= 0;
}
COMMAND_2(map_set_up, RID, p_map, Vector3, p_up) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
map->set_up(p_up);
}
Vector3 GodotNavigationServer::map_get_up(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector3());
return map->get_up();
}
COMMAND_2(map_set_cell_size, RID, p_map, real_t, p_cell_size) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
map->set_cell_size(p_cell_size);
}
real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, 0);
return map->get_cell_size();
}
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
map->set_edge_connection_margin(p_connection_margin);
}
real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, 0);
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 {
- const NavMap *map = map_owner.getornull(p_map);
+ 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);
}
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 {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector3());
return map->get_closest_point_to_segment(p_from, p_to, p_use_collision);
}
Vector3 GodotNavigationServer::map_get_closest_point(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector3());
return map->get_closest_point(p_point);
}
Vector3 GodotNavigationServer::map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector3());
return map->get_closest_point_normal(p_point);
}
RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+ const NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, RID());
return map->get_closest_point_owner(p_point);
@@ -243,13 +243,13 @@ RID GodotNavigationServer::region_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
RID rid = region_owner.make_rid();
- NavRegion *reg = region_owner.getornull(rid);
+ NavRegion *reg = region_owner.get_or_null(rid);
reg->set_self(rid);
return rid;
}
COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
if (region->get_map() != nullptr) {
@@ -262,7 +262,7 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
}
if (p_map.is_valid()) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
map->add_region(region);
@@ -271,28 +271,28 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
}
COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
region->set_transform(p_transform);
}
COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers) {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
region->set_layers(p_layers);
}
uint32_t GodotNavigationServer::region_get_layers(RID p_region) const {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(region == nullptr, 0);
return region->get_layers();
}
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh) {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND(region == nullptr);
region->set_mesh(p_nav_mesh);
@@ -309,21 +309,21 @@ void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node
}
int GodotNavigationServer::region_get_connections_count(RID p_region) const {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(!region, 0);
return region->get_connections_count();
}
Vector3 GodotNavigationServer::region_get_connection_pathway_start(RID p_region, int p_connection_id) const {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(!region, Vector3());
return region->get_connection_pathway_start(p_connection_id);
}
Vector3 GodotNavigationServer::region_get_connection_pathway_end(RID p_region, int p_connection_id) const {
- NavRegion *region = region_owner.getornull(p_region);
+ NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_COND_V(!region, Vector3());
return region->get_connection_pathway_end(p_connection_id);
@@ -333,13 +333,13 @@ RID GodotNavigationServer::agent_create() const {
GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
RID rid = agent_owner.make_rid();
- RvoAgent *agent = agent_owner.getornull(rid);
+ RvoAgent *agent = agent_owner.get_or_null(rid);
agent->set_self(rid);
return rid;
}
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
if (agent->get_map()) {
@@ -353,7 +353,7 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
agent->set_map(nullptr);
if (p_map.is_valid()) {
- NavMap *map = map_owner.getornull(p_map);
+ NavMap *map = map_owner.get_or_null(p_map);
ERR_FAIL_COND(map == nullptr);
agent->set_map(map);
@@ -366,77 +366,77 @@ COMMAND_2(agent_set_map, RID, p_agent, RID, p_map) {
}
COMMAND_2(agent_set_neighbor_dist, RID, p_agent, real_t, p_dist) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->neighborDist_ = p_dist;
}
COMMAND_2(agent_set_max_neighbors, RID, p_agent, int, p_count) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxNeighbors_ = p_count;
}
COMMAND_2(agent_set_time_horizon, RID, p_agent, real_t, p_time) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->timeHorizon_ = p_time;
}
COMMAND_2(agent_set_radius, RID, p_agent, real_t, p_radius) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->radius_ = p_radius;
}
COMMAND_2(agent_set_max_speed, RID, p_agent, real_t, p_max_speed) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->maxSpeed_ = p_max_speed;
}
COMMAND_2(agent_set_velocity, RID, p_agent, Vector3, p_velocity) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->velocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
}
COMMAND_2(agent_set_target_velocity, RID, p_agent, Vector3, p_velocity) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->prefVelocity_ = RVO::Vector3(p_velocity.x, p_velocity.y, p_velocity.z);
}
COMMAND_2(agent_set_position, RID, p_agent, Vector3, p_position) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->position_ = RVO::Vector3(p_position.x, p_position.y, p_position.z);
}
COMMAND_2(agent_set_ignore_y, RID, p_agent, bool, p_ignore) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->get_agent()->ignore_y_ = p_ignore;
}
bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND_V(agent == nullptr, false);
return agent->is_map_changed();
}
COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_method, Variant, p_udata) {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+ RvoAgent *agent = agent_owner.get_or_null(p_agent);
ERR_FAIL_COND(agent == nullptr);
agent->set_callback(p_receiver == nullptr ? ObjectID() : p_receiver->get_instance_id(), p_method, p_udata);
@@ -452,7 +452,7 @@ COMMAND_4(agent_set_callback, RID, p_agent, Object *, p_receiver, StringName, p_
COMMAND_1(free, RID, p_object) {
if (map_owner.owns(p_object)) {
- NavMap *map = map_owner.getornull(p_object);
+ NavMap *map = map_owner.get_or_null(p_object);
// Removes any assigned region
std::vector<NavRegion *> regions = map->get_regions();
@@ -469,12 +469,12 @@ COMMAND_1(free, RID, p_object) {
}
int map_index = active_maps.find(map);
- active_maps.remove(map_index);
- active_maps_update_id.remove(map_index);
+ active_maps.remove_at(map_index);
+ active_maps_update_id.remove_at(map_index);
map_owner.free(p_object);
} else if (region_owner.owns(p_object)) {
- NavRegion *region = region_owner.getornull(p_object);
+ NavRegion *region = region_owner.get_or_null(p_object);
// Removes this region from the map if assigned
if (region->get_map() != nullptr) {
@@ -485,7 +485,7 @@ COMMAND_1(free, RID, p_object) {
region_owner.free(p_object);
} else if (agent_owner.owns(p_object)) {
- RvoAgent *agent = agent_owner.getornull(p_object);
+ RvoAgent *agent = agent_owner.get_or_null(p_object);
// Removes this agent from the map if assigned
if (agent->get_map() != nullptr) {
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 3150ca0bc8..0c8f0ed8c9 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -613,17 +613,17 @@ void NavMap::sync() {
}
Vector<gd::Edge::Connection> free_edges;
- for (Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *E = connections.front(); E; E = E->next()) {
- if (E->get().size() == 2) {
+ for (KeyValue<gd::EdgeKey, Vector<gd::Edge::Connection>> &E : connections) {
+ if (E.value.size() == 2) {
// Connect edge that are shared in different polygons.
- gd::Edge::Connection &c1 = E->get().write[0];
- gd::Edge::Connection &c2 = E->get().write[1];
+ gd::Edge::Connection &c1 = E.value.write[0];
+ gd::Edge::Connection &c2 = E.value.write[1];
c1.polygon->edges[c1.edge].connections.push_back(c2);
c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
} else {
- CRASH_COND_MSG(E->get().size() != 1, vformat("Number of connection != 1. Found: %d", E->get().size()));
- free_edges.push_back(E->get()[0]);
+ CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
+ free_edges.push_back(E.value[0]);
}
}
@@ -664,7 +664,7 @@ void NavMap::sync() {
} else {
other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
- if ((self1 - other1).length() > edge_connection_margin) {
+ if (other1.distance_to(self1) > edge_connection_margin) {
continue;
}
@@ -675,7 +675,7 @@ void NavMap::sync() {
} else {
other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
- if ((self2 - other2).length() > edge_connection_margin) {
+ if (other2.distance_to(self2) > edge_connection_margin) {
continue;
}
diff --git a/modules/navigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 905d10c9d4..0bce0fc9f0 100644
--- a/modules/navigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -36,6 +36,7 @@
#include "core/os/thread.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/multimesh_instance_3d.h"
#include "scene/3d/physics_body_3d.h"
#include "scene/resources/box_shape_3d.h"
#include "scene/resources/capsule_shape_3d.h"
@@ -45,14 +46,15 @@
#include "scene/resources/primitive_meshes.h"
#include "scene/resources/shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
-#include "scene/resources/world_margin_shape_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#endif
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
#ifdef MODULE_CSG_ENABLED
#include "modules/csg/csg_shape.h"
#endif
@@ -62,17 +64,17 @@
NavigationMeshGenerator *NavigationMeshGenerator::singleton = nullptr;
-void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies) {
- p_verticies.push_back(p_vec3.x);
- p_verticies.push_back(p_vec3.y);
- p_verticies.push_back(p_vec3.z);
+void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices) {
+ p_vertices.push_back(p_vec3.x);
+ p_vertices.push_back(p_vec3.y);
+ p_vertices.push_back(p_vec3.z);
}
-void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
int current_vertex_count;
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
- current_vertex_count = p_verticies.size() / 3;
+ current_vertex_count = p_vertices.size() / 3;
if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
continue;
@@ -99,7 +101,7 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform
const int *ir = mesh_indices.ptr();
for (int j = 0; j < mesh_vertices.size(); j++) {
- _add_vertex(p_xform.xform(vr[j]), p_verticies);
+ _add_vertex(p_xform.xform(vr[j]), p_vertices);
}
for (int j = 0; j < face_count; j++) {
@@ -111,9 +113,9 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform
} else {
face_count = mesh_vertices.size() / 3;
for (int j = 0; j < face_count; j++) {
- _add_vertex(p_xform.xform(vr[j * 3 + 0]), p_verticies);
- _add_vertex(p_xform.xform(vr[j * 3 + 2]), p_verticies);
- _add_vertex(p_xform.xform(vr[j * 3 + 1]), p_verticies);
+ _add_vertex(p_xform.xform(vr[j * 3 + 0]), p_vertices);
+ _add_vertex(p_xform.xform(vr[j * 3 + 2]), p_vertices);
+ _add_vertex(p_xform.xform(vr[j * 3 + 1]), p_vertices);
p_indices.push_back(current_vertex_count + (j * 3 + 0));
p_indices.push_back(current_vertex_count + (j * 3 + 1));
@@ -123,14 +125,14 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform
}
}
-void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices) {
+void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices) {
int face_count = p_faces.size() / 3;
- int current_vertex_count = p_verticies.size() / 3;
+ int current_vertex_count = p_vertices.size() / 3;
for (int j = 0; j < face_count; j++) {
- _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_verticies);
- _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_verticies);
- _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_verticies);
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 0]), p_vertices);
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 1]), p_vertices);
+ _add_vertex(p_xform.xform(p_faces[j * 3 + 2]), p_vertices);
p_indices.push_back(current_vertex_count + (j * 3 + 0));
p_indices.push_back(current_vertex_count + (j * 3 + 2));
@@ -138,12 +140,27 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
-void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
+void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children) {
if (Object::cast_to<MeshInstance3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(p_node);
Ref<Mesh> mesh = mesh_instance->get_mesh();
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_verticies, p_indices);
+ _add_mesh(mesh, p_accumulated_transform * mesh_instance->get_transform(), p_vertices, p_indices);
+ }
+ }
+
+ 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_accumulated_transform * multimesh->get_instance_transform(i), p_vertices, p_indices);
+ }
}
}
@@ -154,7 +171,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (!meshes.is_empty()) {
Ref<Mesh> mesh = meshes[1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_verticies, p_indices);
+ _add_mesh(mesh, p_accumulated_transform * csg_shape->get_transform(), p_vertices, p_indices);
}
}
}
@@ -212,7 +229,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s);
if (concave_polygon) {
- _add_faces(concave_polygon->get_faces(), transform, p_verticies, p_indices);
+ _add_faces(concave_polygon->get_faces(), transform, p_vertices, p_indices);
}
ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
@@ -235,12 +252,12 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
}
}
- _add_faces(faces, transform, p_verticies, p_indices);
+ _add_faces(faces, transform, p_vertices, p_indices);
}
}
if (mesh.is_valid()) {
- _add_mesh(mesh, transform, p_verticies, p_indices);
+ _add_mesh(mesh, transform, p_vertices, p_indices);
}
}
}
@@ -255,7 +272,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
for (int i = 0; i < meshes.size(); i += 2) {
Ref<Mesh> mesh = meshes[i + 1];
if (mesh.is_valid()) {
- _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_verticies, p_indices);
+ _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_vertices, p_indices);
}
}
}
@@ -268,7 +285,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform3D p_accumulated_transfor
if (p_recurse_children) {
for (int i = 0; i < p_node->get_child_count(); i++) {
- _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_verticies, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
+ _parse_geometry(p_accumulated_transform, p_node->get_child(i), p_vertices, p_indices, p_generate_from, p_collision_mask, p_recurse_children);
}
}
}
@@ -489,7 +506,7 @@ NavigationMeshGenerator::~NavigationMeshGenerator() {
}
void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node) {
- ERR_FAIL_COND(!p_nav_mesh.is_valid());
+ ERR_FAIL_COND_MSG(!p_nav_mesh.is_valid(), "Invalid navigation mesh.");
#ifdef TOOLS_ENABLED
EditorProgress *ep(nullptr);
diff --git a/modules/navigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index 78f1329e3f..dac844c68e 100644
--- a/modules/navigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -49,10 +49,10 @@ class NavigationMeshGenerator : public Object {
protected:
static void _bind_methods();
- static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_verticies);
- static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
- static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
- static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
+ static void _add_vertex(const Vector3 &p_vec3, Vector<float> &p_vertices);
+ static void _add_mesh(const Ref<Mesh> &p_mesh, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
+ static void _add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform, Vector<float> &p_vertices, Vector<int> &p_indices);
+ static void _parse_geometry(Transform3D p_accumulated_transform, Node *p_node, Vector<float> &p_vertices, Vector<int> &p_indices, NavigationMesh::ParsedGeometryType p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
static void _convert_detail_mesh_to_native_navigation_mesh(const rcPolyMeshDetail *p_detail_mesh, Ref<NavigationMesh> p_nav_mesh);
static void _build_recast_navigation_mesh(
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index e415d92498..f15525648f 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -3,9 +3,6 @@
Import("env")
Import("env_modules")
-# Only kept to build the thirdparty library used by the theora and webm
-# modules.
-
env_ogg = env_modules.Clone()
# Thirdparty source files
diff --git a/modules/ogg/config.py b/modules/ogg/config.py
index d22f9454ed..5a417ba8dd 100644
--- a/modules/ogg/config.py
+++ b/modules/ogg/config.py
@@ -4,3 +4,14 @@ def can_build(env, platform):
def configure(env):
pass
+
+
+def get_doc_classes():
+ return [
+ "OGGPacketSequence",
+ "OGGPacketSequencePlayback",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/ogg/doc_classes/OGGPacketSequence.xml b/modules/ogg/doc_classes/OGGPacketSequence.xml
new file mode 100644
index 0000000000..deac5b67e2
--- /dev/null
+++ b/modules/ogg/doc_classes/OGGPacketSequence.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="OGGPacketSequence" inherits="Resource" version="4.0">
+ <brief_description>
+ A sequence of OGG packets.
+ </brief_description>
+ <description>
+ A sequence of OGG packets.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_length" qualifiers="const">
+ <return type="float" />
+ <description>
+ The length of this stream, in seconds.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="granule_positions" type="Array" setter="set_packet_granule_positions" getter="get_packet_granule_positions" default="[]">
+ Contains the granule positions for each page in this packet sequence.
+ </member>
+ <member name="packet_data" type="Array" setter="set_packet_data" getter="get_packet_data" default="[]">
+ Contains the raw packets that make up this OGGPacketSequence.
+ </member>
+ <member name="sampling_rate" type="float" setter="set_sampling_rate" getter="get_sampling_rate" default="0.0">
+ Holds sample rate information about this sequence. Must be set by another class that actually understands the codec.
+ </member>
+ </members>
+</class>
diff --git a/modules/gdnative/doc_classes/StreamPeerGDNative.xml b/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml
index de76277fff..86dee15567 100644
--- a/modules/gdnative/doc_classes/StreamPeerGDNative.xml
+++ b/modules/ogg/doc_classes/OGGPacketSequencePlayback.xml
@@ -1,13 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="StreamPeerGDNative" inherits="StreamPeer" version="4.0">
+<class name="OGGPacketSequencePlayback" inherits="RefCounted" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/ogg/ogg_packet_sequence.cpp b/modules/ogg/ogg_packet_sequence.cpp
new file mode 100644
index 0000000000..abb2b67ab0
--- /dev/null
+++ b/modules/ogg/ogg_packet_sequence.cpp
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* ogg_packet_sequence.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "ogg_packet_sequence.h"
+#include "core/variant/typed_array.h"
+
+void OGGPacketSequence::push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data) {
+ Vector<PackedByteArray> data_stored;
+ for (int i = 0; i < p_data.size(); i++) {
+ data_stored.push_back(p_data[i]);
+ }
+ page_granule_positions.push_back(p_granule_pos);
+ page_data.push_back(data_stored);
+ data_version++;
+}
+
+void OGGPacketSequence::set_packet_data(const Array &p_data) {
+ data_version++; // Update the data version so old playbacks know that they can't rely on us anymore.
+ page_data.clear();
+ for (int page_idx = 0; page_idx < p_data.size(); page_idx++) {
+ // Push a new page. We cleared the vector so this will be at index `page_idx`.
+ page_data.push_back(Vector<PackedByteArray>());
+ TypedArray<PackedByteArray> this_page_data = p_data[page_idx];
+ for (int packet = 0; packet < this_page_data.size(); packet++) {
+ page_data.write[page_idx].push_back(this_page_data[packet]);
+ }
+ }
+}
+
+Array OGGPacketSequence::get_packet_data() const {
+ Array ret;
+ for (const Vector<PackedByteArray> &page : page_data) {
+ Array page_variant;
+ for (const PackedByteArray &packet : page) {
+ page_variant.push_back(packet);
+ }
+ ret.push_back(page_variant);
+ }
+ return ret;
+}
+
+void OGGPacketSequence::set_packet_granule_positions(const Array &p_granule_positions) {
+ data_version++; // Update the data version so old playbacks know that they can't rely on us anymore.
+ page_granule_positions.clear();
+ for (int page_idx = 0; page_idx < p_granule_positions.size(); page_idx++) {
+ int64_t granule_pos = p_granule_positions[page_idx];
+ page_granule_positions.push_back(granule_pos);
+ }
+}
+
+Array OGGPacketSequence::get_packet_granule_positions() const {
+ Array ret;
+ for (int64_t granule_pos : page_granule_positions) {
+ ret.push_back(granule_pos);
+ }
+ return ret;
+}
+
+void OGGPacketSequence::set_sampling_rate(float p_sampling_rate) {
+ sampling_rate = p_sampling_rate;
+}
+
+float OGGPacketSequence::get_sampling_rate() const {
+ return sampling_rate;
+}
+
+int64_t OGGPacketSequence::get_final_granule_pos() const {
+ if (!page_granule_positions.is_empty()) {
+ return page_granule_positions[page_granule_positions.size() - 1];
+ }
+ return -1;
+}
+
+float OGGPacketSequence::get_length() const {
+ int64_t granule_pos = get_final_granule_pos();
+ if (granule_pos < 0) {
+ return 0;
+ }
+ return granule_pos / sampling_rate;
+}
+
+Ref<OGGPacketSequencePlayback> OGGPacketSequence::instance_playback() {
+ Ref<OGGPacketSequencePlayback> playback;
+ playback.instantiate();
+ playback->ogg_packet_sequence = Ref<OGGPacketSequence>(this);
+ playback->data_version = data_version;
+
+ return playback;
+}
+
+void OGGPacketSequence::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_packet_data", "packet_data"), &OGGPacketSequence::set_packet_data);
+ ClassDB::bind_method(D_METHOD("get_packet_data"), &OGGPacketSequence::get_packet_data);
+
+ ClassDB::bind_method(D_METHOD("set_packet_granule_positions", "granule_positions"), &OGGPacketSequence::set_packet_granule_positions);
+ ClassDB::bind_method(D_METHOD("get_packet_granule_positions"), &OGGPacketSequence::get_packet_granule_positions);
+
+ ClassDB::bind_method(D_METHOD("set_sampling_rate", "sampling_rate"), &OGGPacketSequence::set_sampling_rate);
+ ClassDB::bind_method(D_METHOD("get_sampling_rate"), &OGGPacketSequence::get_sampling_rate);
+
+ ClassDB::bind_method(D_METHOD("get_length"), &OGGPacketSequence::get_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "packet_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_data", "get_packet_data");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "granule_positions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_granule_positions", "get_packet_granule_positions");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sampling_rate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_sampling_rate", "get_sampling_rate");
+}
+
+bool OGGPacketSequencePlayback::next_ogg_packet(ogg_packet **p_packet) const {
+ ERR_FAIL_COND_V(data_version != ogg_packet_sequence->data_version, false);
+ ERR_FAIL_COND_V(ogg_packet_sequence->page_data.is_empty(), false);
+ ERR_FAIL_COND_V(ogg_packet_sequence->page_granule_positions.is_empty(), false);
+ // Move on to the next page if need be. This happens first to help simplify seek logic.
+ while (packet_cursor >= ogg_packet_sequence->page_data[page_cursor].size()) {
+ packet_cursor = 0;
+ page_cursor++;
+ if (page_cursor >= ogg_packet_sequence->page_data.size()) {
+ return false;
+ }
+ }
+
+ ERR_FAIL_COND_V(page_cursor >= ogg_packet_sequence->page_data.size(), false);
+
+ packet->b_o_s = page_cursor == 0 && packet_cursor == 0;
+ packet->e_o_s = page_cursor == ogg_packet_sequence->page_data.size() - 1 && packet_cursor == ogg_packet_sequence->page_data[page_cursor].size() - 1;
+ packet->granulepos = packet_cursor == ogg_packet_sequence->page_data[page_cursor].size() - 1 ? ogg_packet_sequence->page_granule_positions[page_cursor] : -1;
+ packet->packetno = packetno++;
+ packet->bytes = ogg_packet_sequence->page_data[page_cursor][packet_cursor].size();
+ packet->packet = (unsigned char *)(ogg_packet_sequence->page_data[page_cursor][packet_cursor].ptr());
+
+ *p_packet = packet;
+
+ packet_cursor++;
+
+ return true;
+}
+
+uint32_t OGGPacketSequencePlayback::seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive) {
+ if (before_page_inclusive == after_page_inclusive) {
+ return before_page_inclusive;
+ }
+ uint32_t actual_middle_page = after_page_inclusive + (before_page_inclusive - after_page_inclusive) / 2;
+ // Complicating the bisection search algorithm, the middle page might not have a packet that ends on it,
+ // which means it might not have a correct granule position. Find a nearby page that does have a packet ending on it.
+ uint32_t bisection_page = -1;
+ for (uint32_t test_page = actual_middle_page; test_page <= before_page_inclusive; test_page++) {
+ if (ogg_packet_sequence->page_data[test_page].size() > 0) {
+ bisection_page = test_page;
+ break;
+ }
+ }
+ // Check if we have to go backwards.
+ if (bisection_page == (unsigned int)-1) {
+ for (uint32_t test_page = actual_middle_page; test_page >= after_page_inclusive; test_page--) {
+ if (ogg_packet_sequence->page_data[test_page].size() > 0) {
+ bisection_page = test_page;
+ break;
+ }
+ }
+ }
+ if (bisection_page == (unsigned int)-1) {
+ return -1;
+ }
+
+ int64_t bisection_granule_pos = ogg_packet_sequence->page_granule_positions[bisection_page];
+ if (granule > bisection_granule_pos) {
+ return seek_page_internal(granule, bisection_page + 1, before_page_inclusive);
+ } else {
+ return seek_page_internal(granule, after_page_inclusive, bisection_page);
+ }
+}
+
+bool OGGPacketSequencePlayback::seek_page(int64_t p_granule_pos) {
+ int correct_page = seek_page_internal(p_granule_pos, 0, ogg_packet_sequence->page_data.size() - 1);
+ if (correct_page == -1) {
+ return false;
+ }
+
+ packet_cursor = 0;
+ page_cursor = correct_page;
+
+ // Don't pretend subsequent packets are contiguous with previous ones.
+ packetno = 0;
+
+ return true;
+}
+
+OGGPacketSequencePlayback::OGGPacketSequencePlayback() {
+ packet = new ogg_packet();
+}
+
+OGGPacketSequencePlayback::~OGGPacketSequencePlayback() {
+ delete packet;
+}
diff --git a/modules/ogg/ogg_packet_sequence.h b/modules/ogg/ogg_packet_sequence.h
new file mode 100644
index 0000000000..b00ada06c1
--- /dev/null
+++ b/modules/ogg/ogg_packet_sequence.h
@@ -0,0 +1,128 @@
+/*************************************************************************/
+/* ogg_packet_sequence.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OGG_PACKET_SEQUENCE_H
+#define OGG_PACKET_SEQUENCE_H
+
+#include "core/io/resource.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/variant/native_ptr.h"
+#include "core/variant/typed_array.h"
+#include "core/variant/variant.h"
+#include "thirdparty/libogg/ogg/ogg.h"
+
+class OGGPacketSequencePlayback;
+
+class OGGPacketSequence : public Resource {
+ GDCLASS(OGGPacketSequence, Resource);
+
+ friend class OGGPacketSequencePlayback;
+
+ // List of pages, each of which is a list of packets on that page. The innermost PackedByteArrays contain complete ogg packets.
+ Vector<Vector<PackedByteArray>> page_data;
+
+ // List of the granule position for each page.
+ Vector<uint64_t> page_granule_positions;
+
+ // The page after the current last page. Similar semantics to an end() iterator.
+ int64_t end_page = 0;
+
+ uint64_t data_version = 0;
+
+ float sampling_rate = 0;
+ float length = 0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ // Pushes information about all the pages that ended on this page.
+ // This should be called for each page, even for pages that no packets ended on.
+ void push_page(int64_t p_granule_pos, const Vector<PackedByteArray> &p_data);
+
+ void set_packet_data(const Array &p_data);
+ Array get_packet_data() const;
+
+ void set_packet_granule_positions(const Array &p_granule_positions);
+ Array get_packet_granule_positions() const;
+
+ // Sets a sampling rate associated with this object. OGGPacketSequence doesn't understand codecs,
+ // so this value is naively stored as a convenience.
+ void set_sampling_rate(float p_sampling_rate);
+
+ // Returns a sampling rate previously set by set_sampling_rate().
+ float get_sampling_rate() const;
+
+ // Returns a length previously set by set_length().
+ float get_length() const;
+
+ // Returns the granule position of the last page in this sequence.
+ int64_t get_final_granule_pos() const;
+
+ Ref<OGGPacketSequencePlayback> instance_playback();
+
+ OGGPacketSequence() {}
+ virtual ~OGGPacketSequence() {}
+};
+
+class OGGPacketSequencePlayback : public RefCounted {
+ GDCLASS(OGGPacketSequencePlayback, RefCounted);
+
+ friend class OGGPacketSequence;
+
+ Ref<OGGPacketSequence> ogg_packet_sequence;
+
+ mutable int64_t page_cursor = 0;
+ mutable int32_t packet_cursor = 0;
+
+ mutable ogg_packet *packet;
+
+ uint64_t data_version;
+
+ mutable int64_t packetno = 0;
+
+ // Recursive bisection search for the correct page.
+ uint32_t seek_page_internal(int64_t granule, uint32_t after_page_inclusive, uint32_t before_page_inclusive);
+
+public:
+ // Calling functions must not modify this packet.
+ // Returns true on success, false on error or if there is no next packet.
+ bool next_ogg_packet(ogg_packet **p_packet) const;
+
+ // Seeks to the page such that the previous page has a granule position less than or equal to this value,
+ // and the current page has a granule position greater than this value.
+ // Returns true on success, false on failure.
+ bool seek_page(int64_t p_granule_pos);
+
+ OGGPacketSequencePlayback();
+ virtual ~OGGPacketSequencePlayback();
+};
+
+#endif // OGG_PACKET_SEQUENCE_H
diff --git a/modules/ogg/register_types.cpp b/modules/ogg/register_types.cpp
index b23ea65378..3448e7063a 100644
--- a/modules/ogg/register_types.cpp
+++ b/modules/ogg/register_types.cpp
@@ -30,8 +30,11 @@
#include "register_types.h"
-// Dummy module as libogg is needed by other modules (vorbis, theora, opus, ...)
+#include "ogg_packet_sequence.h"
-void register_ogg_types() {}
+void register_ogg_types() {
+ GDREGISTER_CLASS(OGGPacketSequence);
+ GDREGISTER_CLASS(OGGPacketSequencePlayback);
+}
void unregister_ogg_types() {}
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index 2ae7f8cad9..16fea228b1 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -8,16 +8,15 @@
NoiseTexture can also generate normal map textures.
The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_image] may return [code]null[/code] if the generation process has not completed yet. In that case, you need to wait for the texture to be generated before accessing the image and the generated byte data:
[codeblock]
- var texture = preload("res://noise.tres")
- yield(texture, "changed")
+ var texture = NoiseTexture.new()
+ texture.noise = OpenSimplexNoise.new()
+ await texture.changed
var image = texture.get_image()
var data = image.get_data()
[/codeblock]
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="as_normal_map" type="bool" setter="set_as_normal_map" getter="is_normal_map" default="false">
If [code]true[/code], the resulting texture contains a normal map created from the original noise interpreted as a bump map.
@@ -42,6 +41,4 @@
Width of the generated texture.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
index c470f3e1ab..604b07b645 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -109,6 +109,4 @@
Seed used to generate random values, different seeds will generate different noise maps.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp
index 9db3f3d5fd..e36dcfcea5 100644
--- a/modules/opensimplex/noise_texture.cpp
+++ b/modules/opensimplex/noise_texture.cpp
@@ -80,7 +80,7 @@ void NoiseTexture::_bind_methods() {
void NoiseTexture::_validate_property(PropertyInfo &property) const {
if (property.name == "bump_strength") {
if (!as_normal_map) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
}
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index 6237b6460d..a0b2a86c41 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -35,9 +35,6 @@
#include "core/io/image.h"
#include "core/object/ref_counted.h"
-#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
-#include "editor/property_editor.h"
class NoiseTexture : public Texture2D {
GDCLASS(NoiseTexture, Texture2D);
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
deleted file mode 100644
index 1437cd86df..0000000000
--- a/modules/opus/SCsub
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-# Only kept to build the thirdparty library used by the webm module.
-# AudioStreamOpus was dropped in 3.0 due to incompatibility with the new audio
-# engine. If you want to port it, fetch it from the Git history.
-
-env_opus = env_modules.Clone()
-
-# Thirdparty source files
-
-thirdparty_obj = []
-
-# Thirdparty source files
-if env["builtin_opus"]:
- thirdparty_dir = "#thirdparty/opus/"
-
- thirdparty_sources = [
- # Sync with opus_sources.mk
- "opus.c",
- "opus_decoder.c",
- "opus_encoder.c",
- "opus_multistream.c",
- "opus_multistream_encoder.c",
- "opus_multistream_decoder.c",
- "repacketizer.c",
- "analysis.c",
- "mlp.c",
- "mlp_data.c",
- # Sync with libopusfile Makefile.am
- "info.c",
- "internal.c",
- "opusfile.c",
- "stream.c",
- # Sync with celt_sources.mk
- "celt/bands.c",
- "celt/celt.c",
- "celt/celt_encoder.c",
- "celt/celt_decoder.c",
- "celt/cwrs.c",
- "celt/entcode.c",
- "celt/entdec.c",
- "celt/entenc.c",
- "celt/kiss_fft.c",
- "celt/laplace.c",
- "celt/mathops.c",
- "celt/mdct.c",
- "celt/modes.c",
- "celt/pitch.c",
- "celt/celt_lpc.c",
- "celt/quant_bands.c",
- "celt/rate.c",
- "celt/vq.c",
- # "celt/arm/arm_celt_map.c",
- # "celt/arm/armcpu.c",
- # "celt/arm/celt_ne10_fft.c",
- # "celt/arm/celt_ne10_mdct.c",
- # "celt/arm/celt_neon_intr.c",
- # Sync with silk_sources.mk
- "silk/CNG.c",
- "silk/code_signs.c",
- "silk/init_decoder.c",
- "silk/decode_core.c",
- "silk/decode_frame.c",
- "silk/decode_parameters.c",
- "silk/decode_indices.c",
- "silk/decode_pulses.c",
- "silk/decoder_set_fs.c",
- "silk/dec_API.c",
- "silk/enc_API.c",
- "silk/encode_indices.c",
- "silk/encode_pulses.c",
- "silk/gain_quant.c",
- "silk/interpolate.c",
- "silk/LP_variable_cutoff.c",
- "silk/NLSF_decode.c",
- "silk/NSQ.c",
- "silk/NSQ_del_dec.c",
- "silk/PLC.c",
- "silk/shell_coder.c",
- "silk/tables_gain.c",
- "silk/tables_LTP.c",
- "silk/tables_NLSF_CB_NB_MB.c",
- "silk/tables_NLSF_CB_WB.c",
- "silk/tables_other.c",
- "silk/tables_pitch_lag.c",
- "silk/tables_pulses_per_block.c",
- "silk/VAD.c",
- "silk/control_audio_bandwidth.c",
- "silk/quant_LTP_gains.c",
- "silk/VQ_WMat_EC.c",
- "silk/HP_variable_cutoff.c",
- "silk/NLSF_encode.c",
- "silk/NLSF_VQ.c",
- "silk/NLSF_unpack.c",
- "silk/NLSF_del_dec_quant.c",
- "silk/process_NLSFs.c",
- "silk/stereo_LR_to_MS.c",
- "silk/stereo_MS_to_LR.c",
- "silk/check_control_input.c",
- "silk/control_SNR.c",
- "silk/init_encoder.c",
- "silk/control_codec.c",
- "silk/A2NLSF.c",
- "silk/ana_filt_bank_1.c",
- "silk/biquad_alt.c",
- "silk/bwexpander_32.c",
- "silk/bwexpander.c",
- "silk/debug.c",
- "silk/decode_pitch.c",
- "silk/inner_prod_aligned.c",
- "silk/lin2log.c",
- "silk/log2lin.c",
- "silk/LPC_analysis_filter.c",
- "silk/LPC_inv_pred_gain.c",
- "silk/table_LSF_cos.c",
- "silk/NLSF2A.c",
- "silk/NLSF_stabilize.c",
- "silk/NLSF_VQ_weights_laroia.c",
- "silk/pitch_est_tables.c",
- "silk/resampler.c",
- "silk/resampler_down2_3.c",
- "silk/resampler_down2.c",
- "silk/resampler_private_AR2.c",
- "silk/resampler_private_down_FIR.c",
- "silk/resampler_private_IIR_FIR.c",
- "silk/resampler_private_up2_HQ.c",
- "silk/resampler_rom.c",
- "silk/sigm_Q15.c",
- "silk/sort.c",
- "silk/sum_sqr_shift.c",
- "silk/stereo_decode_pred.c",
- "silk/stereo_encode_pred.c",
- "silk/stereo_find_predictor.c",
- "silk/stereo_quant_pred.c",
- ]
-
- opus_sources_silk = []
-
- if env["platform"] in ["android", "iphone", "javascript"]:
- env_opus.Append(CPPDEFINES=["FIXED_POINT"])
- opus_sources_silk = [
- "silk/fixed/LTP_analysis_filter_FIX.c",
- "silk/fixed/LTP_scale_ctrl_FIX.c",
- "silk/fixed/corrMatrix_FIX.c",
- "silk/fixed/encode_frame_FIX.c",
- "silk/fixed/find_LPC_FIX.c",
- "silk/fixed/find_LTP_FIX.c",
- "silk/fixed/find_pitch_lags_FIX.c",
- "silk/fixed/find_pred_coefs_FIX.c",
- "silk/fixed/noise_shape_analysis_FIX.c",
- "silk/fixed/prefilter_FIX.c",
- "silk/fixed/process_gains_FIX.c",
- "silk/fixed/regularize_correlations_FIX.c",
- "silk/fixed/residual_energy16_FIX.c",
- "silk/fixed/residual_energy_FIX.c",
- "silk/fixed/solve_LS_FIX.c",
- "silk/fixed/warped_autocorrelation_FIX.c",
- "silk/fixed/apply_sine_window_FIX.c",
- "silk/fixed/autocorr_FIX.c",
- "silk/fixed/burg_modified_FIX.c",
- "silk/fixed/k2a_FIX.c",
- "silk/fixed/k2a_Q16_FIX.c",
- "silk/fixed/pitch_analysis_core_FIX.c",
- "silk/fixed/vector_ops_FIX.c",
- "silk/fixed/schur64_FIX.c",
- "silk/fixed/schur_FIX.c",
- ]
- else:
- opus_sources_silk = [
- "silk/float/apply_sine_window_FLP.c",
- "silk/float/corrMatrix_FLP.c",
- "silk/float/encode_frame_FLP.c",
- "silk/float/find_LPC_FLP.c",
- "silk/float/find_LTP_FLP.c",
- "silk/float/find_pitch_lags_FLP.c",
- "silk/float/find_pred_coefs_FLP.c",
- "silk/float/LPC_analysis_filter_FLP.c",
- "silk/float/LTP_analysis_filter_FLP.c",
- "silk/float/LTP_scale_ctrl_FLP.c",
- "silk/float/noise_shape_analysis_FLP.c",
- "silk/float/prefilter_FLP.c",
- "silk/float/process_gains_FLP.c",
- "silk/float/regularize_correlations_FLP.c",
- "silk/float/residual_energy_FLP.c",
- "silk/float/solve_LS_FLP.c",
- "silk/float/warped_autocorrelation_FLP.c",
- "silk/float/wrappers_FLP.c",
- "silk/float/autocorrelation_FLP.c",
- "silk/float/burg_modified_FLP.c",
- "silk/float/bwexpander_FLP.c",
- "silk/float/energy_FLP.c",
- "silk/float/inner_product_FLP.c",
- "silk/float/k2a_FLP.c",
- "silk/float/levinsondurbin_FLP.c",
- "silk/float/LPC_inv_pred_gain_FLP.c",
- "silk/float/pitch_analysis_core_FLP.c",
- "silk/float/scale_copy_vector_FLP.c",
- "silk/float/scale_vector_FLP.c",
- "silk/float/schur_FLP.c",
- "silk/float/sort_FLP.c",
- ]
-
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources + opus_sources_silk]
-
- # also requires libogg
- if env["builtin_libogg"]:
- env_opus.Prepend(CPPPATH=["#thirdparty/libogg"])
-
- env_opus.Append(CPPDEFINES=["HAVE_CONFIG_H"])
-
- thirdparty_include_paths = [
- "",
- "celt",
- "opus",
- "silk",
- "silk/fixed",
- "silk/float",
- ]
- env_opus.Prepend(CPPPATH=[thirdparty_dir + "/" + dir for dir in thirdparty_include_paths])
-
- if env["platform"] == "android":
- if "android_arch" in env and env["android_arch"] == "armv7":
- env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"])
- elif "android_arch" in env and env["android_arch"] == "arm64v8":
- env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"])
- elif env["platform"] == "iphone":
- if "arch" in env and env["arch"] == "arm":
- env_opus.Append(CPPDEFINES=["OPUS_ARM_OPT"])
- elif "arch" in env and env["arch"] == "arm64":
- env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"])
- elif env["platform"] == "osx":
- if "arch" in env and env["arch"] == "arm64":
- env_opus.Append(CPPDEFINES=["OPUS_ARM64_OPT"])
-
- env_thirdparty = env_opus.Clone()
- env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- env.modules_sources += thirdparty_obj
-
-
-# Godot source files
-
-module_obj = []
-
-env_opus.add_source_files(module_obj, "*.cpp")
-env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/opus/config.py b/modules/opus/config.py
deleted file mode 100644
index 9ff7b2dece..0000000000
--- a/modules/opus/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return env.module_check_dependencies("opus", ["ogg"])
-
-
-def configure(env):
- pass
diff --git a/modules/opus/register_types.cpp b/modules/opus/register_types.cpp
deleted file mode 100644
index 02874a9a4b..0000000000
--- a/modules/opus/register_types.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "register_types.h"
-
-// Dummy module as libvorbis is needed by other modules (theora ...)
-
-void register_opus_types() {}
-
-void unregister_opus_types() {}
diff --git a/modules/opus/register_types.h b/modules/opus/register_types.h
deleted file mode 100644
index af889cf809..0000000000
--- a/modules/opus/register_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef OPUS_REGISTER_TYPES_H
-#define OPUS_REGISTER_TYPES_H
-
-void register_opus_types();
-void unregister_opus_types();
-
-#endif // OPUS_REGISTER_TYPES_H
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index cb12976090..ffa900ef99 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -390,15 +390,15 @@ static void get_modulation_value(int x, int y, const int p_2bit, const int p_mod
rep_vals0[p_modulation[y + 1][x]] +
rep_vals0[p_modulation[y][x - 1]] +
rep_vals0[p_modulation[y][x + 1]] + 2) /
- 4;
+ 4;
} else if (p_modulation_modes[y][x] == 2) {
mod_val = (rep_vals0[p_modulation[y][x - 1]] +
rep_vals0[p_modulation[y][x + 1]] + 1) /
- 2;
+ 2;
} else {
mod_val = (rep_vals0[p_modulation[y - 1][x]] +
rep_vals0[p_modulation[y + 1][x]] + 1) /
- 2;
+ 2;
}
} else {
mod_val = rep_vals1[p_modulation[y][x]];
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
index 6e7b3e7b8d..ef4c598194 100644
--- a/modules/raycast/SCsub
+++ b/modules/raycast/SCsub
@@ -55,6 +55,9 @@ if env["builtin_embree"]:
"kernels/bvh/bvh_builder_sah_mb.cpp",
"kernels/bvh/bvh_builder_twolevel.cpp",
"kernels/bvh/bvh_intersector1_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_filters.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in embree_src]
@@ -76,6 +79,7 @@ if env["builtin_embree"]:
env.Append(LIBS=["psapi"])
env_thirdparty = env_raycast.Clone()
+ env_thirdparty.force_optimization_on_debug()
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
@@ -83,6 +87,20 @@ if env["builtin_embree"]:
# Embree needs those, it will automatically use SSE2NEON in ARM
env_thirdparty.Append(CPPDEFINES=["__SSE2__", "__SSE__"])
+ if not env.msvc:
+ env_thirdparty.Append(
+ CPPFLAGS=[
+ "-fno-strict-overflow",
+ "-fno-delete-null-pointer-checks",
+ "-fwrapv",
+ "-fsigned-char",
+ "-fno-strict-aliasing",
+ "-fno-tree-vectorize",
+ "-fvisibility=hidden",
+ "-fvisibility-inlines-hidden",
+ ]
+ )
+
env.modules_sources += thirdparty_obj
diff --git a/modules/raycast/config.py b/modules/raycast/config.py
index 5de36c5322..7e8b3e9840 100644
--- a/modules/raycast/config.py
+++ b/modules/raycast/config.py
@@ -1,5 +1,7 @@
def can_build(env, platform):
# Depends on Embree library, which only supports x86_64 and aarch64.
+ if env["arch"].startswith("rv") or env["arch"].startswith("ppc"):
+ return False
if platform == "android":
return env["android_arch"] in ["arm64v8", "x86_64"]
diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py
index 31a25a318f..e31d88b741 100644
--- a/modules/raycast/godot_update_embree.py
+++ b/modules/raycast/godot_update_embree.py
@@ -61,6 +61,11 @@ cpp_files = [
"kernels/bvh/bvh_builder_twolevel.cpp",
"kernels/bvh/bvh_intersector1.cpp",
"kernels/bvh/bvh_intersector1_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_hybrid4_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_bvh4.cpp",
+ "kernels/bvh/bvh_intersector_stream_filters.cpp",
+ "kernels/bvh/bvh_intersector_hybrid.cpp",
+ "kernels/bvh/bvh_intersector_stream.cpp",
]
os.chdir("../../thirdparty")
@@ -117,7 +122,7 @@ with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
/* #undef EMBREE_GEOMETRY_INSTANCE */
/* #undef EMBREE_GEOMETRY_GRID */
/* #undef EMBREE_GEOMETRY_POINT */
-/* #undef EMBREE_RAY_PACKETS */
+#define EMBREE_RAY_PACKETS
/* #undef EMBREE_COMPACT_POLYS */
#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
@@ -249,3 +254,8 @@ with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as con
os.chdir("..")
shutil.rmtree("embree-tmp")
+
+subprocess.run(["git", "restore", "embree/patches"])
+
+for patch in os.listdir("embree/patches"):
+ subprocess.run(["git", "apply", "embree/patches/" + patch])
diff --git a/modules/raycast/lightmap_raycaster.cpp b/modules/raycast/lightmap_raycaster.cpp
index 0583acc119..fdcf509da8 100644
--- a/modules/raycast/lightmap_raycaster.cpp
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -168,7 +168,7 @@ void LightmapRaycasterEmbree::clear_mesh_filter() {
filter_meshes.clear();
}
-void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
+void embree_lm_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
print_error("Embree error: " + String(p_str));
}
@@ -179,16 +179,11 @@ LightmapRaycasterEmbree::LightmapRaycasterEmbree() {
#endif
embree_device = rtcNewDevice(nullptr);
- rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
+ rtcSetDeviceErrorFunction(embree_device, &embree_lm_error_handler, nullptr);
embree_scene = rtcNewScene(embree_device);
}
LightmapRaycasterEmbree::~LightmapRaycasterEmbree() {
-#ifdef __SSE2__
- _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
- _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
-#endif
-
if (embree_scene != nullptr) {
rtcReleaseScene(embree_scene);
}
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h
index 4c3de27837..290b0a1cf3 100644
--- a/modules/raycast/lightmap_raycaster.h
+++ b/modules/raycast/lightmap_raycaster.h
@@ -30,9 +30,9 @@
#ifdef TOOLS_ENABLED
+#include "core/io/image.h"
#include "core/object/object.h"
#include "scene/3d/lightmapper.h"
-#include "scene/resources/mesh.h"
#include <embree3/rtcore.h>
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
index 88c0145ebc..75491c98e5 100644
--- a/modules/raycast/raycast_occlusion_cull.cpp
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -41,9 +41,14 @@ RaycastOcclusionCull *RaycastOcclusionCull::raycast_singleton = nullptr;
void RaycastOcclusionCull::RaycastHZBuffer::clear() {
HZBuffer::clear();
- camera_rays.clear();
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ camera_rays_unaligned_buffer = nullptr;
+ camera_rays = nullptr;
+ }
camera_ray_masks.clear();
- packs_size = Size2i();
+ camera_rays_tile_count = 0;
+ tile_grid_size = Size2i();
}
void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
@@ -58,100 +63,112 @@ void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
HZBuffer::resize(p_size);
- packs_size = Size2i(Math::ceil(p_size.x / (float)TILE_SIZE), Math::ceil(p_size.y / (float)TILE_SIZE));
- int ray_packets_count = packs_size.x * packs_size.y;
- camera_rays.resize(ray_packets_count);
- camera_ray_masks.resize(ray_packets_count * TILE_SIZE * TILE_SIZE);
+ tile_grid_size = Size2i(Math::ceil(p_size.x / (float)TILE_SIZE), Math::ceil(p_size.y / (float)TILE_SIZE));
+ camera_rays_tile_count = tile_grid_size.x * tile_grid_size.y;
+
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ }
+
+ const int alignment = 64; // Embree requires ray packets to be 64-aligned
+ camera_rays_unaligned_buffer = (uint8_t *)memalloc(camera_rays_tile_count * sizeof(CameraRayTile) + alignment);
+ camera_rays = (CameraRayTile *)(camera_rays_unaligned_buffer + alignment - (((uint64_t)camera_rays_unaligned_buffer) % alignment));
+
+ camera_ray_masks.resize(camera_rays_tile_count * TILE_RAYS);
+ memset(camera_ray_masks.ptr(), ~0, camera_rays_tile_count * TILE_RAYS * sizeof(uint32_t));
}
void RaycastOcclusionCull::RaycastHZBuffer::update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool) {
CameraRayThreadData td;
- td.camera_matrix = p_cam_projection;
- td.camera_transform = p_cam_transform;
- td.camera_orthogonal = p_cam_orthogonal;
td.thread_count = p_thread_work_pool.get_thread_count();
+ td.z_near = p_cam_projection.get_z_near();
+ td.z_far = p_cam_projection.get_z_far() * 1.05f;
+ td.camera_pos = p_cam_transform.origin;
+ td.camera_dir = -p_cam_transform.basis.get_axis(2);
+ td.camera_orthogonal = p_cam_orthogonal;
+
+ CameraMatrix inv_camera_matrix = p_cam_projection.inverse();
+ Vector3 camera_corner_proj = Vector3(-1.0f, -1.0f, -1.0f);
+ Vector3 camera_corner_view = inv_camera_matrix.xform(camera_corner_proj);
+ td.pixel_corner = p_cam_transform.xform(camera_corner_view);
+
+ Vector3 top_corner_proj = Vector3(-1.0f, 1.0f, -1.0f);
+ Vector3 top_corner_view = inv_camera_matrix.xform(top_corner_proj);
+ Vector3 top_corner_world = p_cam_transform.xform(top_corner_view);
+
+ Vector3 left_corner_proj = Vector3(1.0f, -1.0f, -1.0f);
+ Vector3 left_corner_view = inv_camera_matrix.xform(left_corner_proj);
+ Vector3 left_corner_world = p_cam_transform.xform(left_corner_view);
+
+ td.pixel_u_interp = left_corner_world - td.pixel_corner;
+ td.pixel_v_interp = top_corner_world - td.pixel_corner;
+
+ debug_tex_range = td.z_far;
+
p_thread_work_pool.do_work(td.thread_count, this, &RaycastHZBuffer::_camera_rays_threaded, &td);
}
-void RaycastOcclusionCull::RaycastHZBuffer::_camera_rays_threaded(uint32_t p_thread, RaycastOcclusionCull::RaycastHZBuffer::CameraRayThreadData *p_data) {
- uint32_t packs_total = camera_rays.size();
+void RaycastOcclusionCull::RaycastHZBuffer::_camera_rays_threaded(uint32_t p_thread, const CameraRayThreadData *p_data) {
+ uint32_t total_tiles = camera_rays_tile_count;
uint32_t total_threads = p_data->thread_count;
- uint32_t from = p_thread * packs_total / total_threads;
- uint32_t to = (p_thread + 1 == total_threads) ? packs_total : ((p_thread + 1) * packs_total / total_threads);
- _generate_camera_rays(p_data->camera_transform, p_data->camera_matrix, p_data->camera_orthogonal, from, to);
+ uint32_t from = p_thread * total_tiles / total_threads;
+ uint32_t to = (p_thread + 1 == total_threads) ? total_tiles : ((p_thread + 1) * total_tiles / total_threads);
+ _generate_camera_rays(p_data, from, to);
}
-void RaycastOcclusionCull::RaycastHZBuffer::_generate_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, int p_from, int p_to) {
- Size2i buffer_size = sizes[0];
-
- CameraMatrix inv_camera_matrix = p_cam_projection.inverse();
- float z_far = p_cam_projection.get_z_far() * 1.05f;
- debug_tex_range = z_far;
-
- RayPacket *ray_packets = camera_rays.ptr();
- uint32_t *ray_masks = camera_ray_masks.ptr();
+void RaycastOcclusionCull::RaycastHZBuffer::_generate_camera_rays(const CameraRayThreadData *p_data, int p_from, int p_to) {
+ const Size2i &buffer_size = sizes[0];
for (int i = p_from; i < p_to; i++) {
- RayPacket &packet = ray_packets[i];
- int tile_x = (i % packs_size.x) * TILE_SIZE;
- int tile_y = (i / packs_size.x) * TILE_SIZE;
+ CameraRayTile &tile = camera_rays[i];
+ int tile_x = (i % tile_grid_size.x) * TILE_SIZE;
+ int tile_y = (i / tile_grid_size.x) * TILE_SIZE;
for (int j = 0; j < TILE_RAYS; j++) {
- float x = tile_x + j % TILE_SIZE;
- float y = tile_y + j / TILE_SIZE;
+ int x = tile_x + j % TILE_SIZE;
+ int y = tile_y + j / TILE_SIZE;
- ray_masks[i * TILE_RAYS + j] = ~0U;
+ float u = (float(x) + 0.5f) / buffer_size.x;
+ float v = (float(y) + 0.5f) / buffer_size.y;
+ Vector3 pixel_pos = p_data->pixel_corner + u * p_data->pixel_u_interp + v * p_data->pixel_v_interp;
- if (x >= buffer_size.x || y >= buffer_size.y) {
- ray_masks[i * TILE_RAYS + j] = 0U;
- } else {
- float u = x / (buffer_size.x - 1);
- float v = y / (buffer_size.y - 1);
- u = u * 2.0f - 1.0f;
- v = v * 2.0f - 1.0f;
-
- Plane pixel_proj = Plane(u, v, -1.0, 1.0);
- Plane pixel_view = inv_camera_matrix.xform4(pixel_proj);
- Vector3 pixel_world = p_cam_transform.xform(pixel_view.normal);
-
- Vector3 dir;
- if (p_cam_orthogonal) {
- dir = -p_cam_transform.basis.get_axis(2);
- } else {
- dir = (pixel_world - p_cam_transform.origin).normalized();
- }
-
- packet.ray.org_x[j] = pixel_world.x;
- packet.ray.org_y[j] = pixel_world.y;
- packet.ray.org_z[j] = pixel_world.z;
+ tile.ray.tnear[j] = p_data->z_near;
- packet.ray.dir_x[j] = dir.x;
- packet.ray.dir_y[j] = dir.y;
- packet.ray.dir_z[j] = dir.z;
+ Vector3 dir;
+ if (p_data->camera_orthogonal) {
+ dir = -p_data->camera_dir;
+ tile.ray.org_x[j] = pixel_pos.x - dir.x * p_data->z_near;
+ tile.ray.org_y[j] = pixel_pos.y - dir.y * p_data->z_near;
+ tile.ray.org_z[j] = pixel_pos.z - dir.z * p_data->z_near;
+ } else {
+ dir = (pixel_pos - p_data->camera_pos).normalized();
+ tile.ray.org_x[j] = p_data->camera_pos.x;
+ tile.ray.org_y[j] = p_data->camera_pos.y;
+ tile.ray.org_z[j] = p_data->camera_pos.z;
+ tile.ray.tnear[j] /= dir.dot(p_data->camera_dir);
+ }
- packet.ray.tnear[j] = 0.0f;
+ tile.ray.dir_x[j] = dir.x;
+ tile.ray.dir_y[j] = dir.y;
+ tile.ray.dir_z[j] = dir.z;
- packet.ray.time[j] = 0.0f;
+ tile.ray.tfar[j] = p_data->z_far;
+ tile.ray.time[j] = 0.0f;
- packet.ray.flags[j] = 0;
- packet.ray.mask[j] = -1;
- packet.hit.geomID[j] = RTC_INVALID_GEOMETRY_ID;
- }
-
- packet.ray.tfar[j] = z_far;
+ tile.ray.flags[j] = 0;
+ tile.ray.mask[j] = ~0U;
+ tile.hit.geomID[j] = RTC_INVALID_GEOMETRY_ID;
}
}
}
-void RaycastOcclusionCull::RaycastHZBuffer::sort_rays() {
- if (is_empty()) {
- return;
- }
+void RaycastOcclusionCull::RaycastHZBuffer::sort_rays(const Vector3 &p_camera_dir, bool p_orthogonal) {
+ ERR_FAIL_COND(is_empty());
Size2i buffer_size = sizes[0];
- for (int i = 0; i < packs_size.y; i++) {
- for (int j = 0; j < packs_size.x; j++) {
+ for (int i = 0; i < tile_grid_size.y; i++) {
+ for (int j = 0; j < tile_grid_size.x; j++) {
for (int tile_i = 0; tile_i < TILE_SIZE; tile_i++) {
for (int tile_j = 0; tile_j < TILE_SIZE; tile_j++) {
int x = j * TILE_SIZE + tile_j;
@@ -160,14 +177,30 @@ void RaycastOcclusionCull::RaycastHZBuffer::sort_rays() {
continue;
}
int k = tile_i * TILE_SIZE + tile_j;
- int packet_index = i * packs_size.x + j;
- mips[0][y * buffer_size.x + x] = camera_rays[packet_index].ray.tfar[k];
+ int tile_index = i * tile_grid_size.x + j;
+ float d = camera_rays[tile_index].ray.tfar[k];
+
+ if (!p_orthogonal) {
+ const float &dir_x = camera_rays[tile_index].ray.dir_x[k];
+ const float &dir_y = camera_rays[tile_index].ray.dir_y[k];
+ const float &dir_z = camera_rays[tile_index].ray.dir_z[k];
+ float cos_theta = p_camera_dir.x * dir_x + p_camera_dir.y * dir_y + p_camera_dir.z * dir_z;
+ d *= cos_theta;
+ }
+
+ mips[0][y * buffer_size.x + x] = d;
}
}
}
}
}
+RaycastOcclusionCull::RaycastHZBuffer::~RaycastHZBuffer() {
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ }
+}
+
////////////////////////////////////////////////////////
bool RaycastOcclusionCull::is_occluder(RID p_rid) {
@@ -184,7 +217,7 @@ void RaycastOcclusionCull::occluder_initialize(RID p_occluder) {
}
void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {
- Occluder *occluder = occluder_owner.getornull(p_occluder);
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
ERR_FAIL_COND(!occluder);
occluder->vertices = p_vertices;
@@ -205,7 +238,7 @@ void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3
}
void RaycastOcclusionCull::free_occluder(RID p_occluder) {
- Occluder *occluder = occluder_owner.getornull(p_occluder);
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
ERR_FAIL_COND(!occluder);
memdelete(occluder);
occluder_owner.free(p_occluder);
@@ -245,7 +278,7 @@ void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance,
bool changed = false;
if (instance.occluder != p_occluder) {
- Occluder *old_occluder = occluder_owner.getornull(instance.occluder);
+ Occluder *old_occluder = occluder_owner.get_or_null(instance.occluder);
if (old_occluder) {
old_occluder->users.erase(InstanceID(p_scenario, p_instance));
}
@@ -253,7 +286,7 @@ void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance,
instance.occluder = p_occluder;
if (p_occluder.is_valid()) {
- Occluder *occluder = occluder_owner.getornull(p_occluder);
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
ERR_FAIL_COND(!occluder);
occluder->users.insert(InstanceID(p_scenario, p_instance));
}
@@ -285,7 +318,7 @@ void RaycastOcclusionCull::scenario_remove_instance(RID p_scenario, RID p_instan
OccluderInstance &instance = scenario.instances[p_instance];
if (!instance.removed) {
- Occluder *occluder = occluder_owner.getornull(instance.occluder);
+ Occluder *occluder = occluder_owner.get_or_null(instance.occluder);
if (occluder) {
occluder->users.erase(InstanceID(p_scenario, p_instance));
}
@@ -307,7 +340,7 @@ void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_in
return;
}
- Occluder *occ = raycast_singleton->occluder_owner.getornull(occ_inst->occluder);
+ Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
if (!occ) {
return;
@@ -423,7 +456,7 @@ bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
const RID *inst_rid = nullptr;
while ((inst_rid = instances.next(inst_rid))) {
OccluderInstance *occ_inst = instances.getptr(*inst_rid);
- Occluder *occ = raycast_singleton->occluder_owner.getornull(occ_inst->occluder);
+ Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
if (!occ || !occ_inst->enabled) {
continue;
@@ -451,7 +484,7 @@ void RaycastOcclusionCull::Scenario::_raycast(uint32_t p_idx, const RaycastThrea
rtcIntersect16((const int *)&p_raycast_data->masks[p_idx * TILE_RAYS], ebr_scene[current_scene_idx], &ctx, &p_raycast_data->rays[p_idx]);
}
-void RaycastOcclusionCull::Scenario::raycast(LocalVector<RayPacket> &r_rays, const LocalVector<uint32_t> p_valid_masks, ThreadWorkPool &p_thread_pool) const {
+void RaycastOcclusionCull::Scenario::raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const {
ERR_FAIL_COND(singleton == nullptr);
if (raycast_singleton->ebr_device == nullptr) {
return; // Embree is initialized on demand when there is some scenario with occluders in it.
@@ -462,10 +495,10 @@ void RaycastOcclusionCull::Scenario::raycast(LocalVector<RayPacket> &r_rays, con
}
RaycastThreadData td;
- td.rays = r_rays.ptr();
- td.masks = p_valid_masks.ptr();
+ td.rays = r_rays;
+ td.masks = p_valid_masks;
- p_thread_pool.do_work(r_rays.size(), this, &Scenario::_raycast, &td);
+ p_thread_pool.do_work(p_tile_count, this, &Scenario::_raycast, &td);
}
////////////////////////////////////////////////////////
@@ -513,8 +546,8 @@ void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_
buffer.update_camera_rays(p_cam_transform, p_cam_projection, p_cam_orthogonal, p_thread_pool);
- scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks, p_thread_pool);
- buffer.sort_rays();
+ scenario.raycast(buffer.camera_rays, buffer.camera_ray_masks.ptr(), buffer.camera_rays_tile_count, p_thread_pool);
+ buffer.sort_rays(-p_cam_transform.basis.get_axis(2), p_cam_orthogonal);
buffer.update_mips();
}
@@ -569,13 +602,15 @@ RaycastOcclusionCull::~RaycastOcclusionCull() {
scenario.commit_thread->wait_to_finish();
memdelete(scenario.commit_thread);
}
+
+ for (int i = 0; i < 2; i++) {
+ if (scenario.ebr_scene[i]) {
+ rtcReleaseScene(scenario.ebr_scene[i]);
+ }
+ }
}
if (ebr_device != nullptr) {
-#ifdef __SSE2__
- _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_OFF);
- _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_OFF);
-#endif
rtcReleaseDevice(ebr_device);
}
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
index 85710a790c..ea96df5ff6 100644
--- a/modules/raycast/raycast_occlusion_cull.h
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -43,33 +43,42 @@
#include <embree3/rtcore.h>
class RaycastOcclusionCull : public RendererSceneOcclusionCull {
- typedef RTCRayHit16 RayPacket;
+ typedef RTCRayHit16 CameraRayTile;
public:
class RaycastHZBuffer : public HZBuffer {
private:
- Size2i packs_size;
+ Size2i tile_grid_size;
struct CameraRayThreadData {
- CameraMatrix camera_matrix;
- Transform3D camera_transform;
- bool camera_orthogonal;
int thread_count;
+ float z_near;
+ float z_far;
+ Vector3 camera_dir;
+ Vector3 camera_pos;
+ Vector3 pixel_corner;
+ Vector3 pixel_u_interp;
+ Vector3 pixel_v_interp;
+ bool camera_orthogonal;
Size2i buffer_size;
};
- void _camera_rays_threaded(uint32_t p_thread, CameraRayThreadData *p_data);
- void _generate_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, int p_from, int p_to);
+ void _camera_rays_threaded(uint32_t p_thread, const CameraRayThreadData *p_data);
+ void _generate_camera_rays(const CameraRayThreadData *p_data, int p_from, int p_to);
public:
- LocalVector<RayPacket> camera_rays;
+ unsigned int camera_rays_tile_count = 0;
+ uint8_t *camera_rays_unaligned_buffer = nullptr;
+ CameraRayTile *camera_rays = nullptr;
LocalVector<uint32_t> camera_ray_masks;
RID scenario_rid;
virtual void clear() override;
virtual void resize(const Size2i &p_size) override;
- void sort_rays();
+ void sort_rays(const Vector3 &p_camera_dir, bool p_orthogonal);
void update_camera_rays(const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_work_pool);
+
+ ~RaycastHZBuffer();
};
private:
@@ -106,7 +115,7 @@ private:
struct Scenario {
struct RaycastThreadData {
- RayPacket *rays;
+ CameraRayTile *rays;
const uint32_t *masks;
};
@@ -139,7 +148,7 @@ private:
bool update(ThreadWorkPool &p_thread_pool);
void _raycast(uint32_t p_thread, const RaycastThreadData *p_raycast_data) const;
- void raycast(LocalVector<RayPacket> &r_rays, const LocalVector<uint32_t> p_valid_masks, ThreadWorkPool &p_thread_pool) const;
+ void raycast(CameraRayTile *r_rays, const uint32_t *p_valid_masks, uint32_t p_tile_count, ThreadWorkPool &p_thread_pool) const;
};
static RaycastOcclusionCull *raycast_singleton;
diff --git a/modules/raycast/register_types.cpp b/modules/raycast/register_types.cpp
index 78ca91309f..ed99e635e1 100644
--- a/modules/raycast/register_types.cpp
+++ b/modules/raycast/register_types.cpp
@@ -32,12 +32,14 @@
#include "lightmap_raycaster.h"
#include "raycast_occlusion_cull.h"
+#include "static_raycaster.h"
RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
void register_raycast_types() {
#ifdef TOOLS_ENABLED
LightmapRaycasterEmbree::make_default_raycaster();
+ StaticRaycasterEmbree::make_default_raycaster();
#endif
raycast_occlusion_cull = memnew(RaycastOcclusionCull);
}
@@ -46,4 +48,7 @@ void unregister_raycast_types() {
if (raycast_occlusion_cull) {
memdelete(raycast_occlusion_cull);
}
+#ifdef TOOLS_ENABLED
+ StaticRaycasterEmbree::free();
+#endif
}
diff --git a/modules/raycast/static_raycaster.cpp b/modules/raycast/static_raycaster.cpp
new file mode 100644
index 0000000000..2ba65eebf8
--- /dev/null
+++ b/modules/raycast/static_raycaster.cpp
@@ -0,0 +1,137 @@
+/*************************************************************************/
+/* static_raycaster.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "static_raycaster.h"
+
+#ifdef __SSE2__
+#include <pmmintrin.h>
+#endif
+
+RTCDevice StaticRaycasterEmbree::embree_device;
+
+StaticRaycaster *StaticRaycasterEmbree::create_embree_raycaster() {
+ return memnew(StaticRaycasterEmbree);
+}
+
+void StaticRaycasterEmbree::make_default_raycaster() {
+ create_function = create_embree_raycaster;
+}
+
+void StaticRaycasterEmbree::free() {
+ if (embree_device) {
+ rtcReleaseDevice(embree_device);
+ }
+}
+
+bool StaticRaycasterEmbree::intersect(Ray &r_ray) {
+ RTCIntersectContext context;
+ rtcInitIntersectContext(&context);
+ rtcIntersect1(embree_scene, &context, (RTCRayHit *)&r_ray);
+ return r_ray.geomID != RTC_INVALID_GEOMETRY_ID;
+}
+
+void StaticRaycasterEmbree::intersect(Vector<Ray> &r_rays) {
+ Ray *rays = r_rays.ptrw();
+ for (int i = 0; i < r_rays.size(); ++i) {
+ intersect(rays[i]);
+ }
+}
+
+void StaticRaycasterEmbree::add_mesh(const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices, unsigned int p_id) {
+ RTCGeometry embree_mesh = rtcNewGeometry(embree_device, RTC_GEOMETRY_TYPE_TRIANGLE);
+
+ int vertex_count = p_vertices.size();
+
+ Vector3 *embree_vertices = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
+ memcpy(embree_vertices, p_vertices.ptr(), sizeof(Vector3) * vertex_count);
+
+ if (p_indices.is_empty()) {
+ ERR_FAIL_COND(vertex_count % 3 != 0);
+ uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, vertex_count / 3);
+ for (int i = 0; i < vertex_count; i++) {
+ embree_triangles[i] = i;
+ }
+ } else {
+ uint32_t *embree_triangles = (uint32_t *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(uint32_t) * 3, p_indices.size() / 3);
+ memcpy(embree_triangles, p_indices.ptr(), sizeof(uint32_t) * p_indices.size());
+ }
+
+ rtcCommitGeometry(embree_mesh);
+ rtcAttachGeometryByID(embree_scene, embree_mesh, p_id);
+ rtcReleaseGeometry(embree_mesh);
+}
+
+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()));
+ }
+ 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()));
+ }
+ rtcCommitScene(embree_scene);
+ filter_meshes.clear();
+}
+
+void embree_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
+ print_error("Embree error: " + String(p_str));
+}
+
+StaticRaycasterEmbree::StaticRaycasterEmbree() {
+#ifdef __SSE2__
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#endif
+
+ if (!embree_device) {
+ embree_device = rtcNewDevice(nullptr);
+ rtcSetDeviceErrorFunction(embree_device, &embree_error_handler, nullptr);
+ }
+
+ embree_scene = rtcNewScene(embree_device);
+}
+
+StaticRaycasterEmbree::~StaticRaycasterEmbree() {
+ if (embree_scene != nullptr) {
+ rtcReleaseScene(embree_scene);
+ }
+}
+
+#endif
diff --git a/modules/stb_vorbis/register_types.cpp b/modules/raycast/static_raycaster.h
index bdb1cf69cf..6b13ecf690 100644
--- a/modules/stb_vorbis/register_types.cpp
+++ b/modules/raycast/static_raycaster.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* register_types.cpp */
+/* static_raycaster.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,25 +28,37 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "register_types.h"
+#ifdef TOOLS_ENABLED
-#include "audio_stream_ogg_vorbis.h"
+#include "core/math/static_raycaster.h"
-#ifdef TOOLS_ENABLED
-#include "core/config/engine.h"
-#include "resource_importer_ogg_vorbis.h"
-#endif
+#include <embree3/rtcore.h>
-void register_stb_vorbis_types() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint()) {
- Ref<ResourceImporterOGGVorbis> ogg_import;
- ogg_import.instantiate();
- ResourceFormatImporter::get_singleton()->add_importer(ogg_import);
- }
-#endif
- GDREGISTER_CLASS(AudioStreamOGGVorbis);
-}
+class StaticRaycasterEmbree : public StaticRaycaster {
+ GDCLASS(StaticRaycasterEmbree, StaticRaycaster);
+
+private:
+ static RTCDevice embree_device;
+ RTCScene embree_scene;
+
+ Set<int> filter_meshes;
-void unregister_stb_vorbis_types() {
-}
+public:
+ virtual bool intersect(Ray &p_ray) override;
+ virtual void intersect(Vector<Ray> &r_rays) override;
+
+ 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 clear_mesh_filter() override;
+
+ static StaticRaycaster *create_embree_raycaster();
+ static void make_default_raycaster();
+ static void free();
+
+ StaticRaycasterEmbree();
+ ~StaticRaycasterEmbree();
+};
+
+#endif
diff --git a/modules/regex/config.py b/modules/regex/config.py
index df9f44cb95..1248a8374d 100644
--- a/modules/regex/config.py
+++ b/modules/regex/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["arch"].startswith("rv")
def configure(env):
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index 9c84974ff6..2ae2e53b02 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -116,6 +116,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index 3cde2836cc..20680b41fd 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -51,6 +51,4 @@
The source string used with the search pattern to find this matching result.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub
deleted file mode 100644
index 8fddb23dc8..0000000000
--- a/modules/stb_vorbis/SCsub
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_stb_vorbis = env_modules.Clone()
-
-# Thirdparty source files
-
-thirdparty_obj = []
-
-thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"]
-
-env_thirdparty = env_stb_vorbis.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-env.modules_sources += thirdparty_obj
-
-# Godot source files
-
-module_obj = []
-
-env_stb_vorbis.add_source_files(module_obj, "*.cpp")
-env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp b/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
deleted file mode 100644
index 3a938200e9..0000000000
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*************************************************************************/
-/* audio_stream_ogg_vorbis.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "audio_stream_ogg_vorbis.h"
-
-#include "core/io/file_access.h"
-
-int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- ERR_FAIL_COND_V(!active, 0);
-
- int todo = p_frames;
-
- int start_buffer = 0;
-
- int frames_mixed_this_step = p_frames;
-
- while (todo && active) {
- float *buffer = (float *)p_buffer;
- if (start_buffer > 0) {
- buffer = (buffer + start_buffer * 2);
- }
- int mixed = stb_vorbis_get_samples_float_interleaved(ogg_stream, 2, buffer, todo * 2);
- if (vorbis_stream->channels == 1 && mixed > 0) {
- //mix mono to stereo
- for (int i = start_buffer; i < start_buffer + mixed; i++) {
- p_buffer[i].r = p_buffer[i].l;
- }
- }
- todo -= mixed;
- frames_mixed += mixed;
-
- if (todo) {
- //end of file!
- bool is_not_empty = mixed > 0 || stb_vorbis_stream_length_in_samples(ogg_stream) > 0;
- if (vorbis_stream->loop && is_not_empty) {
- //loop
- seek(vorbis_stream->loop_offset);
- loops++;
- // 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;
-}
-
-float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {
- return vorbis_stream->sample_rate;
-}
-
-void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {
- active = true;
- seek(p_from_pos);
- loops = 0;
- _begin_resample();
-}
-
-void AudioStreamPlaybackOGGVorbis::stop() {
- active = false;
-}
-
-bool AudioStreamPlaybackOGGVorbis::is_playing() const {
- return active;
-}
-
-int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
- return loops;
-}
-
-float AudioStreamPlaybackOGGVorbis::get_playback_position() const {
- return float(frames_mixed) / vorbis_stream->sample_rate;
-}
-
-void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
- if (!active) {
- return;
- }
-
- if (p_time >= vorbis_stream->get_length()) {
- p_time = 0;
- }
- frames_mixed = uint32_t(vorbis_stream->sample_rate * p_time);
-
- stb_vorbis_seek(ogg_stream, frames_mixed);
-}
-
-AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
- if (ogg_alloc.alloc_buffer) {
- stb_vorbis_close(ogg_stream);
- memfree(ogg_alloc.alloc_buffer);
- }
-}
-
-Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
- Ref<AudioStreamPlaybackOGGVorbis> ovs;
-
- ERR_FAIL_COND_V_MSG(data == nullptr, ovs,
- "This AudioStreamOGGVorbis does not have an audio file assigned "
- "to it. AudioStreamOGGVorbis should not be created from the "
- "inspector or with `.new()`. Instead, load an audio file.");
-
- ovs.instantiate();
- ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this);
- ovs->ogg_alloc.alloc_buffer = (char *)memalloc(decode_mem_size);
- ovs->ogg_alloc.alloc_buffer_length_in_bytes = decode_mem_size;
- ovs->frames_mixed = 0;
- ovs->active = false;
- ovs->loops = 0;
- int error;
- ovs->ogg_stream = stb_vorbis_open_memory((const unsigned char *)data, data_len, &error, &ovs->ogg_alloc);
- if (!ovs->ogg_stream) {
- memfree(ovs->ogg_alloc.alloc_buffer);
- ovs->ogg_alloc.alloc_buffer = nullptr;
- ERR_FAIL_COND_V(!ovs->ogg_stream, Ref<AudioStreamPlaybackOGGVorbis>());
- }
-
- return ovs;
-}
-
-String AudioStreamOGGVorbis::get_stream_name() const {
- return ""; //return stream_name;
-}
-
-void AudioStreamOGGVorbis::clear_data() {
- if (data) {
- memfree(data);
- data = nullptr;
- data_len = 0;
- }
-}
-
-void AudioStreamOGGVorbis::set_data(const Vector<uint8_t> &p_data) {
- int src_data_len = p_data.size();
- uint32_t alloc_try = 1024;
- Vector<char> alloc_mem;
- char *w;
- stb_vorbis *ogg_stream = nullptr;
- stb_vorbis_alloc ogg_alloc;
-
- // Vorbis comments may be up to UINT32_MAX, but that's arguably pretty rare.
- // Let's go with 2^30 so we don't risk going out of bounds.
- const uint32_t MAX_TEST_MEM = 1 << 30;
-
- while (alloc_try < MAX_TEST_MEM) {
- alloc_mem.resize(alloc_try);
- w = alloc_mem.ptrw();
-
- ogg_alloc.alloc_buffer = w;
- ogg_alloc.alloc_buffer_length_in_bytes = alloc_try;
-
- const uint8_t *src_datar = p_data.ptr();
-
- int error;
- ogg_stream = stb_vorbis_open_memory((const unsigned char *)src_datar, src_data_len, &error, &ogg_alloc);
-
- if (!ogg_stream && error == VORBIS_outofmem) {
- alloc_try *= 2;
- } else {
- ERR_FAIL_COND(alloc_try == MAX_TEST_MEM);
- ERR_FAIL_COND(ogg_stream == nullptr);
-
- stb_vorbis_info info = stb_vorbis_get_info(ogg_stream);
-
- channels = info.channels;
- sample_rate = info.sample_rate;
- decode_mem_size = alloc_try;
- //does this work? (it's less mem..)
- //decode_mem_size = ogg_alloc.alloc_buffer_length_in_bytes + info.setup_memory_required + info.temp_memory_required + info.max_frame_size;
-
- length = stb_vorbis_stream_length_in_seconds(ogg_stream);
- stb_vorbis_close(ogg_stream);
-
- // free any existing data
- clear_data();
-
- data = memalloc(src_data_len);
- memcpy(data, src_datar, src_data_len);
- data_len = src_data_len;
-
- break;
- }
- }
-
- ERR_FAIL_COND_MSG(alloc_try == MAX_TEST_MEM, vformat("Couldn't set vorbis data even with an alloc buffer of %d bytes, report bug.", MAX_TEST_MEM));
-}
-
-Vector<uint8_t> AudioStreamOGGVorbis::get_data() const {
- Vector<uint8_t> vdata;
-
- if (data_len && data) {
- vdata.resize(data_len);
- {
- uint8_t *w = vdata.ptrw();
- memcpy(w, data, data_len);
- }
- }
-
- return vdata;
-}
-
-void AudioStreamOGGVorbis::set_loop(bool p_enable) {
- loop = p_enable;
-}
-
-bool AudioStreamOGGVorbis::has_loop() const {
- return loop;
-}
-
-void AudioStreamOGGVorbis::set_loop_offset(float p_seconds) {
- loop_offset = p_seconds;
-}
-
-float AudioStreamOGGVorbis::get_loop_offset() const {
- return loop_offset;
-}
-
-float AudioStreamOGGVorbis::get_length() const {
- return length;
-}
-
-void AudioStreamOGGVorbis::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_data", "data"), &AudioStreamOGGVorbis::set_data);
- ClassDB::bind_method(D_METHOD("get_data"), &AudioStreamOGGVorbis::get_data);
-
- ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
- ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);
-
- ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
- ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
-
- ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
-}
-
-AudioStreamOGGVorbis::AudioStreamOGGVorbis() {}
-
-AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {
- clear_data();
-}
diff --git a/modules/stb_vorbis/config.py b/modules/stb_vorbis/config.py
deleted file mode 100644
index 1eb0a8cf33..0000000000
--- a/modules/stb_vorbis/config.py
+++ /dev/null
@@ -1,16 +0,0 @@
-def can_build(env, platform):
- return True
-
-
-def configure(env):
- pass
-
-
-def get_doc_classes():
- return [
- "AudioStreamOGGVorbis",
- ]
-
-
-def get_doc_path():
- return "doc_classes"
diff --git a/modules/stb_vorbis/register_types.h b/modules/stb_vorbis/register_types.h
deleted file mode 100644
index d36d87606c..0000000000
--- a/modules/stb_vorbis/register_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef STB_VORBIS_REGISTER_TYPES_H
-#define STB_VORBIS_REGISTER_TYPES_H
-
-void register_stb_vorbis_types();
-void unregister_stb_vorbis_types();
-
-#endif // STB_VORBIS_REGISTER_TYPES_H
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
deleted file mode 100644
index 85de698efd..0000000000
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*************************************************************************/
-/* resource_importer_ogg_vorbis.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "resource_importer_ogg_vorbis.h"
-
-#include "core/io/file_access.h"
-#include "core/io/resource_saver.h"
-#include "scene/resources/texture.h"
-
-String ResourceImporterOGGVorbis::get_importer_name() const {
- return "ogg_vorbis";
-}
-
-String ResourceImporterOGGVorbis::get_visible_name() const {
- return "OGGVorbis";
-}
-
-void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ogg");
-}
-
-String ResourceImporterOGGVorbis::get_save_extension() const {
- return "oggstr";
-}
-
-String ResourceImporterOGGVorbis::get_resource_type() const {
- return "AudioStreamOGGVorbis";
-}
-
-bool ResourceImporterOGGVorbis::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
- return true;
-}
-
-int ResourceImporterOGGVorbis::get_preset_count() const {
- return 0;
-}
-
-String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const {
- return String();
-}
-
-void ResourceImporterOGGVorbis::get_import_options(List<ImportOption> *r_options, int p_preset) const {
- r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
- 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) {
- bool loop = p_options["loop"];
- float loop_offset = p_options["loop_offset"];
-
- FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ);
-
- ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
-
- uint64_t len = f->get_length();
-
- Vector<uint8_t> data;
- data.resize(len);
- uint8_t *w = data.ptrw();
-
- f->get_buffer(w, len);
-
- memdelete(f);
-
- Ref<AudioStreamOGGVorbis> ogg_stream;
- ogg_stream.instantiate();
-
- ogg_stream->set_data(data);
- ERR_FAIL_COND_V(!ogg_stream->get_data().size(), ERR_FILE_CORRUPT);
- ogg_stream->set_loop(loop);
- ogg_stream->set_loop_offset(loop_offset);
-
- return ResourceSaver::save(p_save_path + ".oggstr", ogg_stream);
-}
-
-ResourceImporterOGGVorbis::ResourceImporterOGGVorbis() {
-}
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 6691f86e60..d6a96282f3 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -7,7 +7,6 @@ env_text_server_adv = env_modules.Clone()
def make_icu_data(target, source, env):
- import os
dst = target[0].srcnode().abspath
@@ -24,7 +23,6 @@ def make_icu_data(target, source, env):
f = open(source[0].srcnode().abspath, "rb")
buf = f.read()
- import os.path
g.write('extern "C" U_EXPORT const size_t U_ICUDATA_SIZE = ' + str(len(buf)) + ";\n")
g.write('extern "C" U_EXPORT const unsigned char U_ICUDATA_ENTRY_POINT[] = {\n')
@@ -64,6 +62,7 @@ if env["builtin_harfbuzz"]:
#'src/hb-gobject-structs.cc',
"src/hb-icu.cc",
"src/hb-map.cc",
+ "src/hb-ms-feature-ranges.cc",
"src/hb-number.cc",
"src/hb-ot-cff1-table.cc",
"src/hb-ot-cff2-table.cc",
@@ -271,6 +270,7 @@ if env["builtin_icu"]:
"common/dictionarydata.cpp",
"common/dtintrv.cpp",
"common/edits.cpp",
+ "common/emojiprops.cpp",
"common/errorcode.cpp",
"common/filteredbrk.cpp",
"common/filterednormalizer2.cpp",
@@ -292,6 +292,7 @@ if env["builtin_icu"]:
"common/locresdata.cpp",
"common/locutil.cpp",
"common/lsr.cpp",
+ "common/lstmbe.cpp",
"common/messagepattern.cpp",
"common/normalizer2.cpp",
"common/normalizer2impl.cpp",
@@ -449,7 +450,7 @@ if env["builtin_icu"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- icu_data_name = "icudt69l.dat"
+ icu_data_name = "icudt70l.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/config.py b/modules/text_server_adv/config.py
index d22f9454ed..8c8df9b05e 100644
--- a/modules/text_server_adv/config.py
+++ b/modules/text_server_adv/config.py
@@ -4,3 +4,13 @@ def can_build(env, platform):
def configure(env):
pass
+
+
+def get_doc_classes():
+ return [
+ "TextServerAdvanced",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/text_server_adv/doc_classes/TextServerAdvanced.xml b/modules/text_server_adv/doc_classes/TextServerAdvanced.xml
new file mode 100644
index 0000000000..eff4aa5fae
--- /dev/null
+++ b/modules/text_server_adv/doc_classes/TextServerAdvanced.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextServerAdvanced" inherits="TextServer" version="4.0">
+ <brief_description>
+ Text Server using HarfBuzz, ICU and SIL Graphite to support BiDi, complex text layouts and contextual OpenType features.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/modules/text_server_adv/register_types.cpp b/modules/text_server_adv/register_types.cpp
index abefa83b9b..b711d1561f 100644
--- a/modules/text_server_adv/register_types.cpp
+++ b/modules/text_server_adv/register_types.cpp
@@ -33,7 +33,12 @@
#include "text_server_adv.h"
void preregister_text_server_adv_types() {
- TextServerAdvanced::register_server();
+ GDREGISTER_CLASS(TextServerAdvanced);
+ if (TextServerManager::get_singleton()) {
+ Ref<TextServerAdvanced> ts;
+ ts.instantiate();
+ TextServerManager::get_singleton()->add_interface(ts);
+ }
}
void register_text_server_adv_types() {
diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp
index f9bbd25a5f..d1e849def8 100644
--- a/modules/text_server_adv/script_iterator.cpp
+++ b/modules/text_server_adv/script_iterator.cpp
@@ -30,6 +30,8 @@
#include "script_iterator.h"
+// This implementation is derived from ICU: icu4c/source/extra/scrptrun/scrptrun.cpp
+
bool ScriptIterator::same_script(int32_t p_script_one, int32_t p_script_two) {
return p_script_one <= USCRIPT_INHERITED || p_script_two <= USCRIPT_INHERITED || p_script_one == p_script_two;
}
@@ -48,7 +50,8 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
p_start = 0;
}
- ParenStackEntry paren_stack[128];
+ int paren_size = PAREN_STACK_DEPTH;
+ ParenStackEntry *paren_stack = (ParenStackEntry *)memalloc(paren_size * sizeof(ParenStackEntry));
int script_start;
int script_end = p_start;
@@ -64,13 +67,22 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
UChar32 ch = str[script_end];
UScriptCode sc = uscript_getScript(ch, &err);
if (U_FAILURE(err)) {
+ memfree(paren_stack);
ERR_FAIL_MSG(u_errorName(err));
}
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) != U_BPT_NONE) {
if (u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_OPEN) {
- paren_stack[++paren_sp].pair_index = ch;
+ // If it's an open character, push it onto the stack.
+ paren_sp++;
+ if (unlikely(paren_sp >= paren_size)) {
+ // If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text.
+ paren_size += PAREN_STACK_DEPTH;
+ paren_stack = (ParenStackEntry *)memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry));
+ }
+ paren_stack[paren_sp].pair_index = ch;
paren_stack[paren_sp].script_code = script_code;
} else if (paren_sp >= 0) {
+ // If it's a close character, find the matching open on the stack, and use that script code. Any non-matching open characters above it on the stack will be poped.
UChar32 paired_ch = u_getBidiPairedBracket(ch);
while (paren_sp >= 0 && paren_stack[paren_sp].pair_index != paired_ch) {
paren_sp -= 1;
@@ -87,11 +99,13 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
if (same_script(script_code, sc)) {
if (script_code <= USCRIPT_INHERITED && sc > USCRIPT_INHERITED) {
script_code = sc;
+ // Now that we have a final script code, fix any open characters we pushed before we knew the script code.
while (start_sp < paren_sp) {
paren_stack[++start_sp].script_code = script_code;
}
}
if ((u_getIntPropertyValue(ch, UCHAR_BIDI_PAIRED_BRACKET_TYPE) == U_BPT_CLOSE) && paren_sp >= 0) {
+ // If this character is a close paired character pop the matching open character from the stack.
paren_sp -= 1;
if (start_sp >= 0) {
start_sp -= 1;
@@ -109,4 +123,6 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
script_ranges.push_back(rng);
} while (script_end < p_length);
+
+ memfree(paren_stack);
}
diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h
index 896a0e5c15..5efd40f7c4 100644
--- a/modules/text_server_adv/script_iterator.h
+++ b/modules/text_server_adv/script_iterator.h
@@ -43,6 +43,8 @@
#include <hb.h>
class ScriptIterator {
+ static const int PAREN_STACK_DEPTH = 128;
+
public:
struct ScriptRange {
int start = 0;
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 78a87be971..cfab021e24 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -30,6 +30,7 @@
#include "text_server_adv.h"
+#include "core/error/error_macros.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
@@ -37,6 +38,8 @@
#include "thirdparty/icu4c/icudata.gen.h"
#endif
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+
#ifdef MODULE_MSDFGEN_ENABLED
#include "core/ShapeDistanceFinder.h"
#include "core/contour-combiners.h"
@@ -45,13 +48,13 @@
#endif
/*************************************************************************/
-/* hb_bmp_font_t HarfBuzz Bitmap font interface */
+/* bmp_font_t HarfBuzz Bitmap font interface */
/*************************************************************************/
hb_font_funcs_t *TextServerAdvanced::funcs = nullptr;
-TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
- hb_bmp_font_t *bm_font = memnew(hb_bmp_font_t);
+TextServerAdvanced::bmp_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
+ bmp_font_t *bm_font = memnew(bmp_font_t);
if (!bm_font) {
return nullptr;
@@ -63,13 +66,13 @@ TextServerAdvanced::hb_bmp_font_t *TextServerAdvanced::_hb_bmp_font_create(TextS
return bm_font;
}
-void TextServerAdvanced::_hb_bmp_font_destroy(void *p_data) {
- hb_bmp_font_t *bm_font = reinterpret_cast<hb_bmp_font_t *>(p_data);
+void TextServerAdvanced::_bmp_font_destroy(void *p_data) {
+ bmp_font_t *bm_font = reinterpret_cast<bmp_font_t *>(p_data);
memdelete(bm_font);
}
-hb_bool_t TextServerAdvanced::hb_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) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_bool_t TextServerAdvanced::_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) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return false;
@@ -88,8 +91,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_nominal_glyph(hb_font_t *p_font, void *
return true;
}
-hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_position_t TextServerAdvanced::_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return 0;
@@ -102,8 +105,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_advance(hb_font_t *p_font,
return bm_font->face->glyph_map[p_glyph].advance.x * 64;
}
-hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_position_t TextServerAdvanced::_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return 0;
@@ -116,8 +119,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_advance(hb_font_t *p_font,
return -bm_font->face->glyph_map[p_glyph].advance.y * 64;
}
-hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_position_t TextServerAdvanced::_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return 0;
@@ -130,22 +133,8 @@ hb_position_t TextServerAdvanced::hb_bmp_get_glyph_h_kerning(hb_font_t *p_font,
return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].x * 64;
}
-hb_position_t TextServerAdvanced::hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
-
- if (!bm_font->face) {
- return 0;
- }
-
- if (!bm_font->face->kerning_map.has(Vector2i(p_left_glyph, p_right_glyph))) {
- return 0;
- }
-
- return bm_font->face->kerning_map[Vector2i(p_left_glyph, p_right_glyph)].y * 64;
-}
-
-hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_bool_t TextServerAdvanced::_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return false;
@@ -161,8 +150,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void
return true;
}
-hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_bool_t TextServerAdvanced::_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return false;
@@ -180,8 +169,8 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_glyph_extents(hb_font_t *p_font, void *
return true;
}
-hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data) {
- const hb_bmp_font_t *bm_font = reinterpret_cast<const hb_bmp_font_t *>(p_font_data);
+hb_bool_t TextServerAdvanced::_bmp_get_font_h_extents(hb_font_t *p_font, void *p_font_data, hb_font_extents_t *r_metrics, void *p_user_data) {
+ const bmp_font_t *bm_font = reinterpret_cast<const bmp_font_t *>(p_font_data);
if (!bm_font->face) {
return false;
@@ -194,41 +183,40 @@ hb_bool_t TextServerAdvanced::hb_bmp_get_font_h_extents(hb_font_t *p_font, void
return true;
}
-void TextServerAdvanced::hb_bmp_create_font_funcs() {
+void TextServerAdvanced::_bmp_create_font_funcs() {
if (funcs == nullptr) {
funcs = hb_font_funcs_create();
- hb_font_funcs_set_font_h_extents_func(funcs, hb_bmp_get_font_h_extents, nullptr, nullptr);
- hb_font_funcs_set_nominal_glyph_func(funcs, hb_bmp_get_nominal_glyph, nullptr, nullptr);
- hb_font_funcs_set_glyph_h_advance_func(funcs, hb_bmp_get_glyph_h_advance, nullptr, nullptr);
- hb_font_funcs_set_glyph_v_advance_func(funcs, hb_bmp_get_glyph_v_advance, nullptr, nullptr);
- hb_font_funcs_set_glyph_v_origin_func(funcs, hb_bmp_get_glyph_v_origin, nullptr, nullptr);
- hb_font_funcs_set_glyph_h_kerning_func(funcs, hb_bmp_get_glyph_h_kerning, nullptr, nullptr);
- hb_font_funcs_set_glyph_v_kerning_func(funcs, hb_bmp_get_glyph_v_kerning, nullptr, nullptr);
- hb_font_funcs_set_glyph_extents_func(funcs, hb_bmp_get_glyph_extents, nullptr, nullptr);
+ hb_font_funcs_set_font_h_extents_func(funcs, _bmp_get_font_h_extents, nullptr, nullptr);
+ hb_font_funcs_set_nominal_glyph_func(funcs, _bmp_get_nominal_glyph, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_advance_func(funcs, _bmp_get_glyph_h_advance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_advance_func(funcs, _bmp_get_glyph_v_advance, nullptr, nullptr);
+ hb_font_funcs_set_glyph_v_origin_func(funcs, _bmp_get_glyph_v_origin, nullptr, nullptr);
+ hb_font_funcs_set_glyph_h_kerning_func(funcs, _bmp_get_glyph_h_kerning, nullptr, nullptr);
+ hb_font_funcs_set_glyph_extents_func(funcs, _bmp_get_glyph_extents, nullptr, nullptr);
hb_font_funcs_make_immutable(funcs);
}
}
-void TextServerAdvanced::hb_bmp_free_font_funcs() {
+void TextServerAdvanced::_bmp_free_font_funcs() {
if (funcs != nullptr) {
hb_font_funcs_destroy(funcs);
funcs = nullptr;
}
}
-void TextServerAdvanced::_hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref) {
- hb_font_set_funcs(p_font, funcs, _hb_bmp_font_create(p_face, p_unref), _hb_bmp_font_destroy);
+void TextServerAdvanced::_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *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::hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
+hb_font_t *TextServerAdvanced::_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy) {
hb_font_t *font;
hb_face_t *face = hb_face_create(nullptr, 0);
font = hb_font_create(face);
hb_face_destroy(face);
- _hb_bmp_font_set_funcs(font, p_face, false);
+ _bmp_font_set_funcs(font, p_face, false);
return font;
}
@@ -336,7 +324,7 @@ _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
String TextServerAdvanced::interface_name = "ICU / HarfBuzz / Graphite";
uint32_t TextServerAdvanced::interface_features = FEATURE_BIDI_LAYOUT | FEATURE_VERTICAL_LAYOUT | FEATURE_SHAPING | FEATURE_KASHIDA_JUSTIFICATION | FEATURE_BREAK_ITERATORS | FEATURE_USE_SUPPORT_DATA | FEATURE_FONT_VARIABLE;
-bool TextServerAdvanced::has_feature(Feature p_feature) {
+bool TextServerAdvanced::has_feature(Feature p_feature) const {
return (interface_features & p_feature) == p_feature;
}
@@ -344,14 +332,18 @@ String TextServerAdvanced::get_name() const {
return interface_name;
}
+uint32_t TextServerAdvanced::get_features() const {
+ return interface_features;
+}
+
void TextServerAdvanced::free(RID p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataAdvanced *fd = font_owner.getornull(p_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_rid);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_rid);
shaped_owner.free(p_rid);
memdelete(sd);
}
@@ -408,9 +400,23 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) {
return true;
}
-#ifdef TOOLS_ENABLED
+String TextServerAdvanced::get_support_data_filename() const {
+#ifdef ICU_STATIC_DATA
+ return _MKSTR(ICU_DATA_NAME);
+#else
+ return String();
+#endif
+}
+
+String TextServerAdvanced::get_support_data_info() const {
+#ifdef ICU_STATIC_DATA
+ return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(").");
+#else
+ return String();
+#endif
+}
-bool TextServerAdvanced::save_support_data(const String &p_filename) {
+bool TextServerAdvanced::save_support_data(const String &p_filename) const {
_THREAD_SAFE_METHOD_
#ifdef ICU_STATIC_DATA
@@ -429,9 +435,7 @@ bool TextServerAdvanced::save_support_data(const String &p_filename) {
#endif
}
-#endif
-
-bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) {
+bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) const {
String l = p_locale.get_slicec('_', 0);
if ((l == "ar") || (l == "dv") || (l == "he") || (l == "fa") || (l == "ff") || (l == "ku") || (l == "ur")) {
return true;
@@ -440,266 +444,258 @@ bool TextServerAdvanced::is_locale_right_to_left(const String &p_locale) {
}
}
-struct FeatureInfo {
- int32_t tag;
- String name;
-};
-
-static FeatureInfo feature_set[] = {
+void TextServerAdvanced::_insert_feature_sets() {
// Registered OpenType feature tags.
- { HB_TAG('a', 'a', 'l', 't'), "access_all_alternates" },
- { HB_TAG('a', 'b', 'v', 'f'), "above_base_forms" },
- { HB_TAG('a', 'b', 'v', 'm'), "above_base_mark_positioning" },
- { HB_TAG('a', 'b', 'v', 's'), "above_base_substitutions" },
- { HB_TAG('a', 'f', 'r', 'c'), "alternative_fractions" },
- { HB_TAG('a', 'k', 'h', 'n'), "akhands" },
- { HB_TAG('b', 'l', 'w', 'f'), "below_base_forms" },
- { HB_TAG('b', 'l', 'w', 'm'), "below_base_mark_positioning" },
- { HB_TAG('b', 'l', 'w', 's'), "below_base_substitutions" },
- { HB_TAG('c', 'a', 'l', 't'), "contextual_alternates" },
- { HB_TAG('c', 'a', 's', 'e'), "case_sensitive_forms" },
- { HB_TAG('c', 'c', 'm', 'p'), "glyph_composition" },
- { HB_TAG('c', 'f', 'a', 'r'), "conjunct_form_after_ro" },
- { HB_TAG('c', 'j', 'c', 't'), "conjunct_forms" },
- { HB_TAG('c', 'l', 'i', 'g'), "contextual_ligatures" },
- { HB_TAG('c', 'p', 'c', 't'), "centered_cjk_punctuation" },
- { HB_TAG('c', 'p', 's', 'p'), "capital_spacing" },
- { HB_TAG('c', 's', 'w', 'h'), "contextual_swash" },
- { HB_TAG('c', 'u', 'r', 's'), "cursive_positioning" },
- { HB_TAG('c', 'v', '0', '1'), "character_variant_01" },
- { HB_TAG('c', 'v', '0', '2'), "character_variant_02" },
- { HB_TAG('c', 'v', '0', '3'), "character_variant_03" },
- { HB_TAG('c', 'v', '0', '4'), "character_variant_04" },
- { HB_TAG('c', 'v', '0', '5'), "character_variant_05" },
- { HB_TAG('c', 'v', '0', '6'), "character_variant_06" },
- { HB_TAG('c', 'v', '0', '7'), "character_variant_07" },
- { HB_TAG('c', 'v', '0', '8'), "character_variant_08" },
- { HB_TAG('c', 'v', '0', '9'), "character_variant_09" },
- { HB_TAG('c', 'v', '1', '0'), "character_variant_10" },
- { HB_TAG('c', 'v', '1', '1'), "character_variant_11" },
- { HB_TAG('c', 'v', '1', '2'), "character_variant_12" },
- { HB_TAG('c', 'v', '1', '3'), "character_variant_13" },
- { HB_TAG('c', 'v', '1', '4'), "character_variant_14" },
- { HB_TAG('c', 'v', '1', '5'), "character_variant_15" },
- { HB_TAG('c', 'v', '1', '6'), "character_variant_16" },
- { HB_TAG('c', 'v', '1', '7'), "character_variant_17" },
- { HB_TAG('c', 'v', '1', '8'), "character_variant_18" },
- { HB_TAG('c', 'v', '1', '9'), "character_variant_19" },
- { HB_TAG('c', 'v', '2', '0'), "character_variant_20" },
- { HB_TAG('c', 'v', '2', '1'), "character_variant_21" },
- { HB_TAG('c', 'v', '2', '2'), "character_variant_22" },
- { HB_TAG('c', 'v', '2', '3'), "character_variant_23" },
- { HB_TAG('c', 'v', '2', '4'), "character_variant_24" },
- { HB_TAG('c', 'v', '2', '5'), "character_variant_25" },
- { HB_TAG('c', 'v', '2', '6'), "character_variant_26" },
- { HB_TAG('c', 'v', '2', '7'), "character_variant_27" },
- { HB_TAG('c', 'v', '2', '8'), "character_variant_28" },
- { HB_TAG('c', 'v', '2', '9'), "character_variant_29" },
- { HB_TAG('c', 'v', '3', '0'), "character_variant_30" },
- { HB_TAG('c', 'v', '3', '1'), "character_variant_31" },
- { HB_TAG('c', 'v', '3', '2'), "character_variant_32" },
- { HB_TAG('c', 'v', '3', '3'), "character_variant_33" },
- { HB_TAG('c', 'v', '3', '4'), "character_variant_34" },
- { HB_TAG('c', 'v', '3', '5'), "character_variant_35" },
- { HB_TAG('c', 'v', '3', '6'), "character_variant_36" },
- { HB_TAG('c', 'v', '3', '7'), "character_variant_37" },
- { HB_TAG('c', 'v', '3', '8'), "character_variant_38" },
- { HB_TAG('c', 'v', '3', '9'), "character_variant_39" },
- { HB_TAG('c', 'v', '4', '0'), "character_variant_40" },
- { HB_TAG('c', 'v', '4', '1'), "character_variant_41" },
- { HB_TAG('c', 'v', '4', '2'), "character_variant_42" },
- { HB_TAG('c', 'v', '4', '3'), "character_variant_43" },
- { HB_TAG('c', 'v', '4', '4'), "character_variant_44" },
- { HB_TAG('c', 'v', '4', '5'), "character_variant_45" },
- { HB_TAG('c', 'v', '4', '6'), "character_variant_46" },
- { HB_TAG('c', 'v', '4', '7'), "character_variant_47" },
- { HB_TAG('c', 'v', '4', '8'), "character_variant_48" },
- { HB_TAG('c', 'v', '4', '9'), "character_variant_49" },
- { HB_TAG('c', 'v', '5', '0'), "character_variant_50" },
- { HB_TAG('c', 'v', '5', '1'), "character_variant_51" },
- { HB_TAG('c', 'v', '5', '2'), "character_variant_52" },
- { HB_TAG('c', 'v', '5', '3'), "character_variant_53" },
- { HB_TAG('c', 'v', '5', '4'), "character_variant_54" },
- { HB_TAG('c', 'v', '5', '5'), "character_variant_55" },
- { HB_TAG('c', 'v', '5', '6'), "character_variant_56" },
- { HB_TAG('c', 'v', '5', '7'), "character_variant_57" },
- { HB_TAG('c', 'v', '5', '8'), "character_variant_58" },
- { HB_TAG('c', 'v', '5', '9'), "character_variant_59" },
- { HB_TAG('c', 'v', '6', '0'), "character_variant_60" },
- { HB_TAG('c', 'v', '6', '1'), "character_variant_61" },
- { HB_TAG('c', 'v', '6', '2'), "character_variant_62" },
- { HB_TAG('c', 'v', '6', '3'), "character_variant_63" },
- { HB_TAG('c', 'v', '6', '4'), "character_variant_64" },
- { HB_TAG('c', 'v', '6', '5'), "character_variant_65" },
- { HB_TAG('c', 'v', '6', '6'), "character_variant_66" },
- { HB_TAG('c', 'v', '6', '7'), "character_variant_67" },
- { HB_TAG('c', 'v', '6', '8'), "character_variant_68" },
- { HB_TAG('c', 'v', '6', '9'), "character_variant_69" },
- { HB_TAG('c', 'v', '7', '0'), "character_variant_70" },
- { HB_TAG('c', 'v', '7', '1'), "character_variant_71" },
- { HB_TAG('c', 'v', '7', '2'), "character_variant_72" },
- { HB_TAG('c', 'v', '7', '3'), "character_variant_73" },
- { HB_TAG('c', 'v', '7', '4'), "character_variant_74" },
- { HB_TAG('c', 'v', '7', '5'), "character_variant_75" },
- { HB_TAG('c', 'v', '7', '6'), "character_variant_76" },
- { HB_TAG('c', 'v', '7', '7'), "character_variant_77" },
- { HB_TAG('c', 'v', '7', '8'), "character_variant_78" },
- { HB_TAG('c', 'v', '7', '9'), "character_variant_79" },
- { HB_TAG('c', 'v', '8', '0'), "character_variant_80" },
- { HB_TAG('c', 'v', '8', '1'), "character_variant_81" },
- { HB_TAG('c', 'v', '8', '2'), "character_variant_82" },
- { HB_TAG('c', 'v', '8', '3'), "character_variant_83" },
- { HB_TAG('c', 'v', '8', '4'), "character_variant_84" },
- { HB_TAG('c', 'v', '8', '5'), "character_variant_85" },
- { HB_TAG('c', 'v', '8', '6'), "character_variant_86" },
- { HB_TAG('c', 'v', '8', '7'), "character_variant_87" },
- { HB_TAG('c', 'v', '8', '8'), "character_variant_88" },
- { HB_TAG('c', 'v', '8', '9'), "character_variant_89" },
- { HB_TAG('c', 'v', '9', '0'), "character_variant_90" },
- { HB_TAG('c', 'v', '9', '1'), "character_variant_91" },
- { HB_TAG('c', 'v', '9', '2'), "character_variant_92" },
- { HB_TAG('c', 'v', '9', '3'), "character_variant_93" },
- { HB_TAG('c', 'v', '9', '4'), "character_variant_94" },
- { HB_TAG('c', 'v', '9', '5'), "character_variant_95" },
- { HB_TAG('c', 'v', '9', '6'), "character_variant_96" },
- { HB_TAG('c', 'v', '9', '7'), "character_variant_97" },
- { HB_TAG('c', 'v', '9', '8'), "character_variant_98" },
- { HB_TAG('c', 'v', '9', '9'), "character_variant_99" },
- { HB_TAG('c', '2', 'p', 'c'), "petite_capitals_from_capitals" },
- { HB_TAG('c', '2', 's', 'c'), "small_capitals_from_capitals" },
- { HB_TAG('d', 'i', 's', 't'), "distances" },
- { HB_TAG('d', 'l', 'i', 'g'), "discretionary_ligatures" },
- { HB_TAG('d', 'n', 'o', 'm'), "denominators" },
- { HB_TAG('d', 't', 'l', 's'), "dotless_forms" },
- { HB_TAG('e', 'x', 'p', 't'), "expert_forms" },
- { HB_TAG('f', 'a', 'l', 't'), "final_glyph_on_line_alternates" },
- { HB_TAG('f', 'i', 'n', '2'), "terminal_forms_2" },
- { HB_TAG('f', 'i', 'n', '3'), "terminal_forms_3" },
- { HB_TAG('f', 'i', 'n', 'a'), "terminal_forms" },
- { HB_TAG('f', 'l', 'a', 'c'), "flattened_accent_forms" },
- { HB_TAG('f', 'r', 'a', 'c'), "fractions" },
- { HB_TAG('f', 'w', 'i', 'd'), "full_widths" },
- { HB_TAG('h', 'a', 'l', 'f'), "half_forms" },
- { HB_TAG('h', 'a', 'l', 'n'), "halant_forms" },
- { HB_TAG('h', 'a', 'l', 't'), "alternate_half_widths" },
- { HB_TAG('h', 'i', 's', 't'), "historical_forms" },
- { HB_TAG('h', 'k', 'n', 'a'), "horizontal_kana_alternates" },
- { HB_TAG('h', 'l', 'i', 'g'), "historical_ligatures" },
- { HB_TAG('h', 'n', 'g', 'l'), "hangul" },
- { HB_TAG('h', 'o', 'j', 'o'), "hojo_kanji_forms" },
- { HB_TAG('h', 'w', 'i', 'd'), "half_widths" },
- { HB_TAG('i', 'n', 'i', 't'), "initial_forms" },
- { HB_TAG('i', 's', 'o', 'l'), "isolated_forms" },
- { HB_TAG('i', 't', 'a', 'l'), "italics" },
- { HB_TAG('j', 'a', 'l', 't'), "justification_alternates" },
- { HB_TAG('j', 'p', '7', '8'), "jis78_forms" },
- { HB_TAG('j', 'p', '8', '3'), "jis83_forms" },
- { HB_TAG('j', 'p', '9', '0'), "jis90_forms" },
- { HB_TAG('j', 'p', '0', '4'), "jis2004_forms" },
- { HB_TAG('k', 'e', 'r', 'n'), "kerning" },
- { HB_TAG('l', 'f', 'b', 'd'), "left_bounds" },
- { HB_TAG('l', 'i', 'g', 'a'), "standard_ligatures" },
- { HB_TAG('l', 'j', 'm', 'o'), "leading_jamo_forms" },
- { HB_TAG('l', 'n', 'u', 'm'), "lining_figures" },
- { HB_TAG('l', 'o', 'c', 'l'), "localized_forms" },
- { HB_TAG('l', 't', 'r', 'a'), "left_to_right_alternates" },
- { HB_TAG('l', 't', 'r', 'm'), "left_to_right_mirrored_forms" },
- { HB_TAG('m', 'a', 'r', 'k'), "mark_positioning" },
- { HB_TAG('m', 'e', 'd', '2'), "medial_forms_2" },
- { HB_TAG('m', 'e', 'd', 'i'), "medial_forms" },
- { HB_TAG('m', 'g', 'r', 'k'), "mathematical_greek" },
- { HB_TAG('m', 'k', 'm', 'k'), "mark_to_mark_positioning" },
- { HB_TAG('m', 's', 'e', 't'), "mark_positioning_via_substitution" },
- { HB_TAG('n', 'a', 'l', 't'), "alternate_annotation_forms" },
- { HB_TAG('n', 'l', 'c', 'k'), "nlc_kanji_forms" },
- { HB_TAG('n', 'u', 'k', 't'), "nukta_forms" },
- { HB_TAG('n', 'u', 'm', 'r'), "numerators" },
- { HB_TAG('o', 'n', 'u', 'm'), "oldstyle_figures" },
- { HB_TAG('o', 'p', 'b', 'd'), "optical_bounds" },
- { HB_TAG('o', 'r', 'd', 'n'), "ordinals" },
- { HB_TAG('o', 'r', 'n', 'm'), "ornaments" },
- { HB_TAG('p', 'a', 'l', 't'), "proportional_alternate_widths" },
- { HB_TAG('p', 'c', 'a', 'p'), "petite_capitals" },
- { HB_TAG('p', 'k', 'n', 'a'), "proportional_kana" },
- { HB_TAG('p', 'n', 'u', 'm'), "proportional_figures" },
- { HB_TAG('p', 'r', 'e', 'f'), "pre_base_forms" },
- { HB_TAG('p', 'r', 'e', 's'), "pre_base_substitutions" },
- { HB_TAG('p', 's', 't', 'f'), "post_base_forms" },
- { HB_TAG('p', 's', 't', 's'), "post_base_substitutions" },
- { HB_TAG('p', 'w', 'i', 'd'), "proportional_widths" },
- { HB_TAG('q', 'w', 'i', 'd'), "quarter_widths" },
- { HB_TAG('r', 'a', 'n', 'd'), "randomize" },
- { HB_TAG('r', 'c', 'l', 't'), "required_contextual_alternates" },
- { HB_TAG('r', 'k', 'r', 'f'), "rakar_forms" },
- { HB_TAG('r', 'l', 'i', 'g'), "required_ligatures" },
- { HB_TAG('r', 'p', 'h', 'f'), "reph_forms" },
- { HB_TAG('r', 't', 'b', 'd'), "right_bounds" },
- { HB_TAG('r', 't', 'l', 'a'), "right_to_left_alternates" },
- { HB_TAG('r', 't', 'l', 'm'), "right_to_left_mirrored_forms" },
- { HB_TAG('r', 'u', 'b', 'y'), "ruby_notation_forms" },
- { HB_TAG('r', 'v', 'r', 'n'), "required_variation_alternates" },
- { HB_TAG('s', 'a', 'l', 't'), "stylistic_alternates" },
- { HB_TAG('s', 'i', 'n', 'f'), "scientific_inferiors" },
- { HB_TAG('s', 'i', 'z', 'e'), "optical_size" },
- { HB_TAG('s', 'm', 'c', 'p'), "small_capitals" },
- { HB_TAG('s', 'm', 'p', 'l'), "simplified_forms" },
- { HB_TAG('s', 's', '0', '1'), "stylistic_set_01" },
- { HB_TAG('s', 's', '0', '2'), "stylistic_set_02" },
- { HB_TAG('s', 's', '0', '3'), "stylistic_set_03" },
- { HB_TAG('s', 's', '0', '4'), "stylistic_set_04" },
- { HB_TAG('s', 's', '0', '5'), "stylistic_set_05" },
- { HB_TAG('s', 's', '0', '6'), "stylistic_set_06" },
- { HB_TAG('s', 's', '0', '7'), "stylistic_set_07" },
- { HB_TAG('s', 's', '0', '8'), "stylistic_set_08" },
- { HB_TAG('s', 's', '0', '9'), "stylistic_set_09" },
- { HB_TAG('s', 's', '1', '0'), "stylistic_set_10" },
- { HB_TAG('s', 's', '1', '1'), "stylistic_set_11" },
- { HB_TAG('s', 's', '1', '2'), "stylistic_set_12" },
- { HB_TAG('s', 's', '1', '3'), "stylistic_set_13" },
- { HB_TAG('s', 's', '1', '4'), "stylistic_set_14" },
- { HB_TAG('s', 's', '1', '5'), "stylistic_set_15" },
- { HB_TAG('s', 's', '1', '6'), "stylistic_set_16" },
- { HB_TAG('s', 's', '1', '7'), "stylistic_set_17" },
- { HB_TAG('s', 's', '1', '8'), "stylistic_set_18" },
- { HB_TAG('s', 's', '1', '9'), "stylistic_set_19" },
- { HB_TAG('s', 's', '2', '0'), "stylistic_set_20" },
- { HB_TAG('s', 's', 't', 'y'), "math_script_style_alternates" },
- { HB_TAG('s', 't', 'c', 'h'), "stretching_glyph_decomposition" },
- { HB_TAG('s', 'u', 'b', 's'), "subscript" },
- { HB_TAG('s', 'u', 'p', 's'), "superscript" },
- { HB_TAG('s', 'w', 's', 'h'), "swash" },
- { HB_TAG('t', 'i', 't', 'l'), "titling" },
- { HB_TAG('t', 'j', 'm', 'o'), "trailing_jamo_forms" },
- { HB_TAG('t', 'n', 'a', 'm'), "traditional_name_forms" },
- { HB_TAG('t', 'n', 'u', 'm'), "tabular_figures" },
- { HB_TAG('t', 'r', 'a', 'd'), "traditional_forms" },
- { HB_TAG('t', 'w', 'i', 'd'), "third_widths" },
- { HB_TAG('u', 'n', 'i', 'c'), "unicase" },
- { HB_TAG('v', 'a', 'l', 't'), "alternate_vertical_metrics" },
- { HB_TAG('v', 'a', 't', 'u'), "vattu_variants" },
- { HB_TAG('v', 'e', 'r', 't'), "vertical_writing" },
- { HB_TAG('v', 'h', 'a', 'l'), "alternate_vertical_half_metrics" },
- { HB_TAG('v', 'j', 'm', 'o'), "vowel_jamo_forms" },
- { HB_TAG('v', 'k', 'n', 'a'), "vertical_kana_alternates" },
- { HB_TAG('v', 'k', 'r', 'n'), "vertical_kerning" },
- { HB_TAG('v', 'p', 'a', 'l'), "proportional_alternate_vertical_metrics" },
- { HB_TAG('v', 'r', 't', '2'), "vertical_alternates_and_rotation" },
- { HB_TAG('v', 'r', 't', 'r'), "vertical_alternates_for_rotation" },
- { HB_TAG('z', 'e', 'r', 'o'), "slashed_zero" },
- // Registered OpenType variation tags.
- { HB_TAG('i', 't', 'a', 'l'), "italic" },
- { HB_TAG('o', 'p', 's', 'z'), "optical_size" },
- { HB_TAG('s', 'l', 'n', 't'), "slant" },
- { HB_TAG('w', 'd', 't', 'h'), "width" },
- { HB_TAG('w', 'g', 'h', 't'), "weight" },
- { 0, String() },
-};
+ feature_sets.insert("access_all_alternates", HB_TAG('a', 'a', 'l', 't'));
+ feature_sets.insert("above_base_forms", HB_TAG('a', 'b', 'v', 'f'));
+ feature_sets.insert("above_base_mark_positioning", HB_TAG('a', 'b', 'v', 'm'));
+ feature_sets.insert("above_base_substitutions", HB_TAG('a', 'b', 'v', 's'));
+ feature_sets.insert("alternative_fractions", HB_TAG('a', 'f', 'r', 'c'));
+ feature_sets.insert("akhands", HB_TAG('a', 'k', 'h', 'n'));
+ feature_sets.insert("below_base_forms", HB_TAG('b', 'l', 'w', 'f'));
+ feature_sets.insert("below_base_mark_positioning", HB_TAG('b', 'l', 'w', 'm'));
+ feature_sets.insert("below_base_substitutions", HB_TAG('b', 'l', 'w', 's'));
+ feature_sets.insert("contextual_alternates", HB_TAG('c', 'a', 'l', 't'));
+ feature_sets.insert("case_sensitive_forms", HB_TAG('c', 'a', 's', 'e'));
+ feature_sets.insert("glyph_composition", HB_TAG('c', 'c', 'm', 'p'));
+ feature_sets.insert("conjunct_form_after_ro", HB_TAG('c', 'f', 'a', 'r'));
+ feature_sets.insert("conjunct_forms", HB_TAG('c', 'j', 'c', 't'));
+ feature_sets.insert("contextual_ligatures", HB_TAG('c', 'l', 'i', 'g'));
+ feature_sets.insert("centered_cjk_punctuation", HB_TAG('c', 'p', 'c', 't'));
+ feature_sets.insert("capital_spacing", HB_TAG('c', 'p', 's', 'p'));
+ feature_sets.insert("contextual_swash", HB_TAG('c', 's', 'w', 'h'));
+ feature_sets.insert("cursive_positioning", HB_TAG('c', 'u', 'r', 's'));
+ feature_sets.insert("character_variant_01", HB_TAG('c', 'v', '0', '1'));
+ feature_sets.insert("character_variant_02", HB_TAG('c', 'v', '0', '2'));
+ feature_sets.insert("character_variant_03", HB_TAG('c', 'v', '0', '3'));
+ feature_sets.insert("character_variant_04", HB_TAG('c', 'v', '0', '4'));
+ feature_sets.insert("character_variant_05", HB_TAG('c', 'v', '0', '5'));
+ feature_sets.insert("character_variant_06", HB_TAG('c', 'v', '0', '6'));
+ feature_sets.insert("character_variant_07", HB_TAG('c', 'v', '0', '7'));
+ feature_sets.insert("character_variant_08", HB_TAG('c', 'v', '0', '8'));
+ feature_sets.insert("character_variant_09", HB_TAG('c', 'v', '0', '9'));
+ feature_sets.insert("character_variant_10", HB_TAG('c', 'v', '1', '0'));
+ feature_sets.insert("character_variant_11", HB_TAG('c', 'v', '1', '1'));
+ feature_sets.insert("character_variant_12", HB_TAG('c', 'v', '1', '2'));
+ feature_sets.insert("character_variant_13", HB_TAG('c', 'v', '1', '3'));
+ feature_sets.insert("character_variant_14", HB_TAG('c', 'v', '1', '4'));
+ feature_sets.insert("character_variant_15", HB_TAG('c', 'v', '1', '5'));
+ feature_sets.insert("character_variant_16", HB_TAG('c', 'v', '1', '6'));
+ feature_sets.insert("character_variant_17", HB_TAG('c', 'v', '1', '7'));
+ feature_sets.insert("character_variant_18", HB_TAG('c', 'v', '1', '8'));
+ feature_sets.insert("character_variant_19", HB_TAG('c', 'v', '1', '9'));
+ feature_sets.insert("character_variant_20", HB_TAG('c', 'v', '2', '0'));
+ feature_sets.insert("character_variant_21", HB_TAG('c', 'v', '2', '1'));
+ feature_sets.insert("character_variant_22", HB_TAG('c', 'v', '2', '2'));
+ feature_sets.insert("character_variant_23", HB_TAG('c', 'v', '2', '3'));
+ feature_sets.insert("character_variant_24", HB_TAG('c', 'v', '2', '4'));
+ feature_sets.insert("character_variant_25", HB_TAG('c', 'v', '2', '5'));
+ feature_sets.insert("character_variant_26", HB_TAG('c', 'v', '2', '6'));
+ feature_sets.insert("character_variant_27", HB_TAG('c', 'v', '2', '7'));
+ feature_sets.insert("character_variant_28", HB_TAG('c', 'v', '2', '8'));
+ feature_sets.insert("character_variant_29", HB_TAG('c', 'v', '2', '9'));
+ feature_sets.insert("character_variant_30", HB_TAG('c', 'v', '3', '0'));
+ feature_sets.insert("character_variant_31", HB_TAG('c', 'v', '3', '1'));
+ feature_sets.insert("character_variant_32", HB_TAG('c', 'v', '3', '2'));
+ feature_sets.insert("character_variant_33", HB_TAG('c', 'v', '3', '3'));
+ feature_sets.insert("character_variant_34", HB_TAG('c', 'v', '3', '4'));
+ feature_sets.insert("character_variant_35", HB_TAG('c', 'v', '3', '5'));
+ feature_sets.insert("character_variant_36", HB_TAG('c', 'v', '3', '6'));
+ feature_sets.insert("character_variant_37", HB_TAG('c', 'v', '3', '7'));
+ feature_sets.insert("character_variant_38", HB_TAG('c', 'v', '3', '8'));
+ feature_sets.insert("character_variant_39", HB_TAG('c', 'v', '3', '9'));
+ feature_sets.insert("character_variant_40", HB_TAG('c', 'v', '4', '0'));
+ feature_sets.insert("character_variant_41", HB_TAG('c', 'v', '4', '1'));
+ feature_sets.insert("character_variant_42", HB_TAG('c', 'v', '4', '2'));
+ feature_sets.insert("character_variant_43", HB_TAG('c', 'v', '4', '3'));
+ feature_sets.insert("character_variant_44", HB_TAG('c', 'v', '4', '4'));
+ feature_sets.insert("character_variant_45", HB_TAG('c', 'v', '4', '5'));
+ feature_sets.insert("character_variant_46", HB_TAG('c', 'v', '4', '6'));
+ feature_sets.insert("character_variant_47", HB_TAG('c', 'v', '4', '7'));
+ feature_sets.insert("character_variant_48", HB_TAG('c', 'v', '4', '8'));
+ feature_sets.insert("character_variant_49", HB_TAG('c', 'v', '4', '9'));
+ feature_sets.insert("character_variant_50", HB_TAG('c', 'v', '5', '0'));
+ feature_sets.insert("character_variant_51", HB_TAG('c', 'v', '5', '1'));
+ feature_sets.insert("character_variant_52", HB_TAG('c', 'v', '5', '2'));
+ feature_sets.insert("character_variant_53", HB_TAG('c', 'v', '5', '3'));
+ feature_sets.insert("character_variant_54", HB_TAG('c', 'v', '5', '4'));
+ feature_sets.insert("character_variant_55", HB_TAG('c', 'v', '5', '5'));
+ feature_sets.insert("character_variant_56", HB_TAG('c', 'v', '5', '6'));
+ feature_sets.insert("character_variant_57", HB_TAG('c', 'v', '5', '7'));
+ feature_sets.insert("character_variant_58", HB_TAG('c', 'v', '5', '8'));
+ feature_sets.insert("character_variant_59", HB_TAG('c', 'v', '5', '9'));
+ feature_sets.insert("character_variant_60", HB_TAG('c', 'v', '6', '0'));
+ feature_sets.insert("character_variant_61", HB_TAG('c', 'v', '6', '1'));
+ feature_sets.insert("character_variant_62", HB_TAG('c', 'v', '6', '2'));
+ feature_sets.insert("character_variant_63", HB_TAG('c', 'v', '6', '3'));
+ feature_sets.insert("character_variant_64", HB_TAG('c', 'v', '6', '4'));
+ feature_sets.insert("character_variant_65", HB_TAG('c', 'v', '6', '5'));
+ feature_sets.insert("character_variant_66", HB_TAG('c', 'v', '6', '6'));
+ feature_sets.insert("character_variant_67", HB_TAG('c', 'v', '6', '7'));
+ feature_sets.insert("character_variant_68", HB_TAG('c', 'v', '6', '8'));
+ feature_sets.insert("character_variant_69", HB_TAG('c', 'v', '6', '9'));
+ feature_sets.insert("character_variant_70", HB_TAG('c', 'v', '7', '0'));
+ feature_sets.insert("character_variant_71", HB_TAG('c', 'v', '7', '1'));
+ feature_sets.insert("character_variant_72", HB_TAG('c', 'v', '7', '2'));
+ feature_sets.insert("character_variant_73", HB_TAG('c', 'v', '7', '3'));
+ feature_sets.insert("character_variant_74", HB_TAG('c', 'v', '7', '4'));
+ feature_sets.insert("character_variant_75", HB_TAG('c', 'v', '7', '5'));
+ feature_sets.insert("character_variant_76", HB_TAG('c', 'v', '7', '6'));
+ feature_sets.insert("character_variant_77", HB_TAG('c', 'v', '7', '7'));
+ feature_sets.insert("character_variant_78", HB_TAG('c', 'v', '7', '8'));
+ feature_sets.insert("character_variant_79", HB_TAG('c', 'v', '7', '9'));
+ feature_sets.insert("character_variant_80", HB_TAG('c', 'v', '8', '0'));
+ feature_sets.insert("character_variant_81", HB_TAG('c', 'v', '8', '1'));
+ feature_sets.insert("character_variant_82", HB_TAG('c', 'v', '8', '2'));
+ feature_sets.insert("character_variant_83", HB_TAG('c', 'v', '8', '3'));
+ feature_sets.insert("character_variant_84", HB_TAG('c', 'v', '8', '4'));
+ feature_sets.insert("character_variant_85", HB_TAG('c', 'v', '8', '5'));
+ feature_sets.insert("character_variant_86", HB_TAG('c', 'v', '8', '6'));
+ feature_sets.insert("character_variant_87", HB_TAG('c', 'v', '8', '7'));
+ feature_sets.insert("character_variant_88", HB_TAG('c', 'v', '8', '8'));
+ feature_sets.insert("character_variant_89", HB_TAG('c', 'v', '8', '9'));
+ feature_sets.insert("character_variant_90", HB_TAG('c', 'v', '9', '0'));
+ feature_sets.insert("character_variant_91", HB_TAG('c', 'v', '9', '1'));
+ feature_sets.insert("character_variant_92", HB_TAG('c', 'v', '9', '2'));
+ feature_sets.insert("character_variant_93", HB_TAG('c', 'v', '9', '3'));
+ feature_sets.insert("character_variant_94", HB_TAG('c', 'v', '9', '4'));
+ feature_sets.insert("character_variant_95", HB_TAG('c', 'v', '9', '5'));
+ feature_sets.insert("character_variant_96", HB_TAG('c', 'v', '9', '6'));
+ feature_sets.insert("character_variant_97", HB_TAG('c', 'v', '9', '7'));
+ feature_sets.insert("character_variant_98", HB_TAG('c', 'v', '9', '8'));
+ feature_sets.insert("character_variant_99", HB_TAG('c', 'v', '9', '9'));
+ feature_sets.insert("petite_capitals_from_capitals", HB_TAG('c', '2', 'p', 'c'));
+ feature_sets.insert("small_capitals_from_capitals", HB_TAG('c', '2', 's', 'c'));
+ feature_sets.insert("distances", HB_TAG('d', 'i', 's', 't'));
+ feature_sets.insert("discretionary_ligatures", HB_TAG('d', 'l', 'i', 'g'));
+ feature_sets.insert("denominators", HB_TAG('d', 'n', 'o', 'm'));
+ feature_sets.insert("dotless_forms", HB_TAG('d', 't', 'l', 's'));
+ feature_sets.insert("expert_forms", HB_TAG('e', 'x', 'p', 't'));
+ feature_sets.insert("final_glyph_on_line_alternates", HB_TAG('f', 'a', 'l', 't'));
+ feature_sets.insert("terminal_forms_2", HB_TAG('f', 'i', 'n', '2'));
+ feature_sets.insert("terminal_forms_3", HB_TAG('f', 'i', 'n', '3'));
+ feature_sets.insert("terminal_forms", HB_TAG('f', 'i', 'n', 'a'));
+ feature_sets.insert("flattened_accent_forms", HB_TAG('f', 'l', 'a', 'c'));
+ feature_sets.insert("fractions", HB_TAG('f', 'r', 'a', 'c'));
+ feature_sets.insert("full_widths", HB_TAG('f', 'w', 'i', 'd'));
+ feature_sets.insert("half_forms", HB_TAG('h', 'a', 'l', 'f'));
+ feature_sets.insert("halant_forms", HB_TAG('h', 'a', 'l', 'n'));
+ feature_sets.insert("alternate_half_widths", HB_TAG('h', 'a', 'l', 't'));
+ feature_sets.insert("historical_forms", HB_TAG('h', 'i', 's', 't'));
+ feature_sets.insert("horizontal_kana_alternates", HB_TAG('h', 'k', 'n', 'a'));
+ feature_sets.insert("historical_ligatures", HB_TAG('h', 'l', 'i', 'g'));
+ feature_sets.insert("hangul", HB_TAG('h', 'n', 'g', 'l'));
+ feature_sets.insert("hojo_kanji_forms", HB_TAG('h', 'o', 'j', 'o'));
+ feature_sets.insert("half_widths", HB_TAG('h', 'w', 'i', 'd'));
+ feature_sets.insert("initial_forms", HB_TAG('i', 'n', 'i', 't'));
+ feature_sets.insert("isolated_forms", HB_TAG('i', 's', 'o', 'l'));
+ feature_sets.insert("italics", HB_TAG('i', 't', 'a', 'l'));
+ feature_sets.insert("justification_alternates", HB_TAG('j', 'a', 'l', 't'));
+ feature_sets.insert("jis78_forms", HB_TAG('j', 'p', '7', '8'));
+ feature_sets.insert("jis83_forms", HB_TAG('j', 'p', '8', '3'));
+ feature_sets.insert("jis90_forms", HB_TAG('j', 'p', '9', '0'));
+ feature_sets.insert("jis2004_forms", HB_TAG('j', 'p', '0', '4'));
+ feature_sets.insert("kerning", HB_TAG('k', 'e', 'r', 'n'));
+ feature_sets.insert("left_bounds", HB_TAG('l', 'f', 'b', 'd'));
+ feature_sets.insert("standard_ligatures", HB_TAG('l', 'i', 'g', 'a'));
+ feature_sets.insert("leading_jamo_forms", HB_TAG('l', 'j', 'm', 'o'));
+ feature_sets.insert("lining_figures", HB_TAG('l', 'n', 'u', 'm'));
+ feature_sets.insert("localized_forms", HB_TAG('l', 'o', 'c', 'l'));
+ feature_sets.insert("left_to_right_alternates", HB_TAG('l', 't', 'r', 'a'));
+ feature_sets.insert("left_to_right_mirrored_forms", HB_TAG('l', 't', 'r', 'm'));
+ feature_sets.insert("mark_positioning", HB_TAG('m', 'a', 'r', 'k'));
+ feature_sets.insert("medial_forms_2", HB_TAG('m', 'e', 'd', '2'));
+ feature_sets.insert("medial_forms", HB_TAG('m', 'e', 'd', 'i'));
+ feature_sets.insert("mathematical_greek", HB_TAG('m', 'g', 'r', 'k'));
+ feature_sets.insert("mark_to_mark_positioning", HB_TAG('m', 'k', 'm', 'k'));
+ feature_sets.insert("mark_positioning_via_substitution", HB_TAG('m', 's', 'e', 't'));
+ feature_sets.insert("alternate_annotation_forms", HB_TAG('n', 'a', 'l', 't'));
+ feature_sets.insert("nlc_kanji_forms", HB_TAG('n', 'l', 'c', 'k'));
+ feature_sets.insert("nukta_forms", HB_TAG('n', 'u', 'k', 't'));
+ feature_sets.insert("numerators", HB_TAG('n', 'u', 'm', 'r'));
+ feature_sets.insert("oldstyle_figures", HB_TAG('o', 'n', 'u', 'm'));
+ feature_sets.insert("optical_bounds", HB_TAG('o', 'p', 'b', 'd'));
+ feature_sets.insert("ordinals", HB_TAG('o', 'r', 'd', 'n'));
+ feature_sets.insert("ornaments", HB_TAG('o', 'r', 'n', 'm'));
+ feature_sets.insert("proportional_alternate_widths", HB_TAG('p', 'a', 'l', 't'));
+ feature_sets.insert("petite_capitals", HB_TAG('p', 'c', 'a', 'p'));
+ feature_sets.insert("proportional_kana", HB_TAG('p', 'k', 'n', 'a'));
+ feature_sets.insert("proportional_figures", HB_TAG('p', 'n', 'u', 'm'));
+ feature_sets.insert("pre_base_forms", HB_TAG('p', 'r', 'e', 'f'));
+ feature_sets.insert("pre_base_substitutions", HB_TAG('p', 'r', 'e', 's'));
+ feature_sets.insert("post_base_forms", HB_TAG('p', 's', 't', 'f'));
+ feature_sets.insert("post_base_substitutions", HB_TAG('p', 's', 't', 's'));
+ feature_sets.insert("proportional_widths", HB_TAG('p', 'w', 'i', 'd'));
+ feature_sets.insert("quarter_widths", HB_TAG('q', 'w', 'i', 'd'));
+ feature_sets.insert("randomize", HB_TAG('r', 'a', 'n', 'd'));
+ feature_sets.insert("required_contextual_alternates", HB_TAG('r', 'c', 'l', 't'));
+ feature_sets.insert("rakar_forms", HB_TAG('r', 'k', 'r', 'f'));
+ feature_sets.insert("required_ligatures", HB_TAG('r', 'l', 'i', 'g'));
+ feature_sets.insert("reph_forms", HB_TAG('r', 'p', 'h', 'f'));
+ feature_sets.insert("right_bounds", HB_TAG('r', 't', 'b', 'd'));
+ feature_sets.insert("right_to_left_alternates", HB_TAG('r', 't', 'l', 'a'));
+ feature_sets.insert("right_to_left_mirrored_forms", HB_TAG('r', 't', 'l', 'm'));
+ feature_sets.insert("ruby_notation_forms", HB_TAG('r', 'u', 'b', 'y'));
+ feature_sets.insert("required_variation_alternates", HB_TAG('r', 'v', 'r', 'n'));
+ feature_sets.insert("stylistic_alternates", HB_TAG('s', 'a', 'l', 't'));
+ feature_sets.insert("scientific_inferiors", HB_TAG('s', 'i', 'n', 'f'));
+ feature_sets.insert("optical_size", HB_TAG('s', 'i', 'z', 'e'));
+ feature_sets.insert("small_capitals", HB_TAG('s', 'm', 'c', 'p'));
+ feature_sets.insert("simplified_forms", HB_TAG('s', 'm', 'p', 'l'));
+ feature_sets.insert("stylistic_set_01", HB_TAG('s', 's', '0', '1'));
+ feature_sets.insert("stylistic_set_02", HB_TAG('s', 's', '0', '2'));
+ feature_sets.insert("stylistic_set_03", HB_TAG('s', 's', '0', '3'));
+ feature_sets.insert("stylistic_set_04", HB_TAG('s', 's', '0', '4'));
+ feature_sets.insert("stylistic_set_05", HB_TAG('s', 's', '0', '5'));
+ feature_sets.insert("stylistic_set_06", HB_TAG('s', 's', '0', '6'));
+ feature_sets.insert("stylistic_set_07", HB_TAG('s', 's', '0', '7'));
+ feature_sets.insert("stylistic_set_08", HB_TAG('s', 's', '0', '8'));
+ feature_sets.insert("stylistic_set_09", HB_TAG('s', 's', '0', '9'));
+ feature_sets.insert("stylistic_set_10", HB_TAG('s', 's', '1', '0'));
+ feature_sets.insert("stylistic_set_11", HB_TAG('s', 's', '1', '1'));
+ feature_sets.insert("stylistic_set_12", HB_TAG('s', 's', '1', '2'));
+ feature_sets.insert("stylistic_set_13", HB_TAG('s', 's', '1', '3'));
+ feature_sets.insert("stylistic_set_14", HB_TAG('s', 's', '1', '4'));
+ feature_sets.insert("stylistic_set_15", HB_TAG('s', 's', '1', '5'));
+ feature_sets.insert("stylistic_set_16", HB_TAG('s', 's', '1', '6'));
+ feature_sets.insert("stylistic_set_17", HB_TAG('s', 's', '1', '7'));
+ feature_sets.insert("stylistic_set_18", HB_TAG('s', 's', '1', '8'));
+ feature_sets.insert("stylistic_set_19", HB_TAG('s', 's', '1', '9'));
+ feature_sets.insert("stylistic_set_20", HB_TAG('s', 's', '2', '0'));
+ feature_sets.insert("math_script_style_alternates", HB_TAG('s', 's', 't', 'y'));
+ feature_sets.insert("stretching_glyph_decomposition", HB_TAG('s', 't', 'c', 'h'));
+ feature_sets.insert("subscript", HB_TAG('s', 'u', 'b', 's'));
+ feature_sets.insert("superscript", HB_TAG('s', 'u', 'p', 's'));
+ feature_sets.insert("swash", HB_TAG('s', 'w', 's', 'h'));
+ feature_sets.insert("titling", HB_TAG('t', 'i', 't', 'l'));
+ feature_sets.insert("trailing_jamo_forms", HB_TAG('t', 'j', 'm', 'o'));
+ feature_sets.insert("traditional_name_forms", HB_TAG('t', 'n', 'a', 'm'));
+ feature_sets.insert("tabular_figures", HB_TAG('t', 'n', 'u', 'm'));
+ feature_sets.insert("traditional_forms", HB_TAG('t', 'r', 'a', 'd'));
+ feature_sets.insert("third_widths", HB_TAG('t', 'w', 'i', 'd'));
+ feature_sets.insert("unicase", HB_TAG('u', 'n', 'i', 'c'));
+ feature_sets.insert("alternate_vertical_metrics", HB_TAG('v', 'a', 'l', 't'));
+ feature_sets.insert("vattu_variants", HB_TAG('v', 'a', 't', 'u'));
+ feature_sets.insert("vertical_writing", HB_TAG('v', 'e', 'r', 't'));
+ feature_sets.insert("alternate_vertical_half_metrics", HB_TAG('v', 'h', 'a', 'l'));
+ feature_sets.insert("vowel_jamo_forms", HB_TAG('v', 'j', 'm', 'o'));
+ feature_sets.insert("vertical_kana_alternates", HB_TAG('v', 'k', 'n', 'a'));
+ feature_sets.insert("vertical_kerning", HB_TAG('v', 'k', 'r', 'n'));
+ feature_sets.insert("proportional_alternate_vertical_metrics", HB_TAG('v', 'p', 'a', 'l'));
+ feature_sets.insert("vertical_alternates_and_rotation", HB_TAG('v', 'r', 't', '2'));
+ feature_sets.insert("vertical_alternates_for_rotation", HB_TAG('v', 'r', 't', 'r'));
+ feature_sets.insert("slashed_zero", HB_TAG('z', 'e', 'r', 'o'));
+ // Registered OpenType variation tag.
+ feature_sets.insert("italic", HB_TAG('i', 't', 'a', 'l'));
+ feature_sets.insert("optical_size", HB_TAG('o', 'p', 's', 'z'));
+ feature_sets.insert("slant", HB_TAG('s', 'l', 'n', 't'));
+ feature_sets.insert("width", HB_TAG('w', 'd', 't', 'h'));
+ feature_sets.insert("weight", HB_TAG('w', 'g', 'h', 't'));
+}
int32_t TextServerAdvanced::name_to_tag(const String &p_name) const {
- for (int i = 0; feature_set[i].tag != 0; i++) {
- if (feature_set[i].name == p_name) {
- return feature_set[i].tag;
- }
+ if (feature_sets.has(p_name)) {
+ return feature_sets[p_name];
}
// No readable name, use tag string.
@@ -707,9 +703,9 @@ int32_t TextServerAdvanced::name_to_tag(const String &p_name) const {
}
String TextServerAdvanced::tag_to_name(int32_t p_tag) const {
- for (int i = 0; feature_set[i].tag != 0; i++) {
- if (feature_set[i].tag == p_tag) {
- return feature_set[i].name;
+ for (const KeyValue<StringName, int32_t> &E : feature_sets) {
+ if (E.value == p_tag) {
+ return E.key;
}
}
@@ -744,6 +740,10 @@ _FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_
continue;
}
+ if (ct.offsets.size() < ct.texture_w) {
+ continue;
+ }
+
ret.y = 0x7FFFFFFF;
ret.x = 0;
@@ -942,7 +942,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
FontGlyph chr;
chr.found = true;
- chr.advance = advance.round();
+ chr.advance = advance;
if (shape.validate() && shape.contours.size() > 0) {
int w = (bounds.r - bounds.l);
@@ -960,7 +960,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
edgeColoringSimple(shape, 3.0); // Max. angle.
msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
- //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation.
DistancePixelConversion distancePixelConversion(p_pixel_range);
msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));
@@ -990,10 +989,6 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(
wr[ofs + 1] = (uint8_t)(CLAMP(image(j, i)[1] * 256.f, 0.f, 255.f));
wr[ofs + 2] = (uint8_t)(CLAMP(image(j, i)[2] * 256.f, 0.f, 255.f));
wr[ofs + 3] = (uint8_t)(CLAMP(image(j, i)[3] * 256.f, 0.f, 255.f));
- //wr[ofs + 0] = 100;
- //wr[ofs + 1] = 100;
- //wr[ofs + 2] = 100;
- //wr[ofs + 3] = 100;
}
}
}
@@ -1058,13 +1053,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
case FT_PIXEL_MODE_MONO: {
int byte = i * bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
- wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
- wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
- //wr[ofs + 1] = 100;
break;
case FT_PIXEL_MODE_BGRA: {
int ofs_color = i * bitmap.pitch + (j << 2);
@@ -1101,12 +1095,12 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma
}
FontGlyph chr;
- chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
chr.found = true;
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
- chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round();
+ chr.rect.position = Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling;
chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
return chr;
}
@@ -1162,7 +1156,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
int error = FT_Load_Glyph(fd->face, p_glyph, flags);
if (error) {
fd->glyph_map[p_glyph] = FontGlyph();
- ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph.");
+ return false;
}
if (!outline) {
@@ -1219,19 +1213,20 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_d
}
_FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
FontDataForSizeAdvanced *fd = memnew(FontDataForSizeAdvanced);
fd->size = p_size;
- if (p_font_data->data_ptr) {
+ if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
int error = 0;
if (!library) {
error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'.");
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
}
memset(&fd->stream, 0, sizeof(FT_StreamRec));
@@ -1249,20 +1244,14 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
- ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'.");
- }
- fd->hb_handle = hb_ft_font_create(fd->face, nullptr);
- if (fd->hb_handle == nullptr) {
- FT_Done_Face(fd->face);
- fd->face = nullptr;
- ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating FreeType font object."));
+ ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
}
if (p_font_data->msdf) {
fd->oversampling = 1.0f;
fd->size.x = p_font_data->msdf_source_size;
} else if (p_font_data->oversampling <= 0.0f) {
- fd->oversampling = TS->font_get_global_oversampling();
+ fd->oversampling = font_get_global_oversampling();
} else {
fd->oversampling = p_font_data->oversampling;
}
@@ -1270,26 +1259,46 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) {
int best_match = 0;
int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width));
- fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
+ fd->scale = float(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
for (int i = 1; i < fd->face->num_fixed_sizes; i++) {
int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width));
if (ndiff < diff) {
best_match = i;
diff = ndiff;
- fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
+ fd->scale = float(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
}
}
FT_Select_Size(fd->face, best_match);
} else {
- FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling);
+ FT_Set_Pixel_Sizes(fd->face, 0, float(fd->size.x * fd->oversampling));
+ fd->scale = ((float)fd->size.x * fd->oversampling) / (float)fd->face->size->metrics.y_ppem;
}
+ fd->hb_handle = hb_ft_font_create(fd->face, nullptr);
+
fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale;
fd->descent = (-fd->face->size->metrics.descender / 64.0) / fd->oversampling * fd->scale;
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;
if (!p_font_data->face_init) {
+ // Get style flags and name.
+ if (fd->face->family_name != nullptr) {
+ p_font_data->font_name = String::utf8((const char *)fd->face->family_name);
+ }
+ if (fd->face->style_name != nullptr) {
+ p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
+ }
+ p_font_data->style_flags = 0;
+ if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ p_font_data->style_flags |= FONT_BOLD;
+ }
+ if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ p_font_data->style_flags |= FONT_ITALIC;
+ }
+ if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
+ p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ }
// Get supported scripts from OpenType font data.
p_font_data->supported_scripts.clear();
unsigned int count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
@@ -1598,22 +1607,19 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_cache_for_size(FontDataAdvanced
FT_Done_MM_Var(library, amaster);
}
#else
- ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
+ ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
} else {
// Init bitmap font.
- fd->hb_handle = hb_bmp_font_create(fd, nullptr);
- if (!fd->hb_handle) {
- ERR_FAIL_V_MSG(false, TTR("HarfBuzz: Error creating bitmap font object."));
- }
+ fd->hb_handle = _bmp_font_create(fd, nullptr);
}
p_font_data->cache[p_size] = fd;
return true;
}
_FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_font_data) {
- for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = p_font_data->cache.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : p_font_data->cache) {
+ memdelete(E.value);
}
p_font_data->cache.clear();
p_font_data->face_init = false;
@@ -1623,7 +1629,7 @@ _FORCE_INLINE_ void TextServerAdvanced::_font_clear_cache(FontDataAdvanced *p_fo
}
hb_font_t *TextServerAdvanced::_font_get_hb_handle(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, nullptr);
MutexLock lock(fd->mutex);
@@ -1641,7 +1647,7 @@ RID TextServerAdvanced::create_font() {
}
void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1652,7 +1658,7 @@ void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_
}
void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1662,8 +1668,68 @@ void TextServerAdvanced::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data
fd->data_size = p_data_size;
}
+void TextServerAdvanced::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerAdvanced::font_get_style(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerAdvanced::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerAdvanced::font_get_style_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerAdvanced::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerAdvanced::font_get_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1674,7 +1740,7 @@ void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased
}
bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1682,7 +1748,7 @@ bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1693,7 +1759,7 @@ void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_
}
bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1701,7 +1767,7 @@ bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_r
}
void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1712,7 +1778,7 @@ void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pi
}
int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1720,7 +1786,7 @@ int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1731,7 +1797,7 @@ void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
}
int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1739,7 +1805,7 @@ int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1749,7 +1815,7 @@ void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
}
int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1757,7 +1823,7 @@ int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1768,7 +1834,7 @@ void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_
}
bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1776,7 +1842,7 @@ bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1787,7 +1853,7 @@ void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_
}
TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -1795,7 +1861,7 @@ TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const {
}
void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1806,15 +1872,15 @@ void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Di
}
Dictionary TextServerAdvanced::font_get_variation_coordinates(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
return fd->variation_coordinates;
}
-void TextServerAdvanced::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_oversampling(RID p_font_rid, float p_oversampling) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1824,8 +1890,8 @@ void TextServerAdvanced::font_set_oversampling(RID p_font_rid, real_t p_oversamp
}
}
-real_t TextServerAdvanced::font_get_oversampling(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_oversampling(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1833,7 +1899,7 @@ real_t TextServerAdvanced::font_get_oversampling(RID p_font_rid) const {
}
Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1845,18 +1911,18 @@ Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const {
}
void TextServerAdvanced::font_clear_size_cache(RID p_font_rid) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ memdelete(E.value);
}
fd->cache.clear();
}
void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1866,8 +1932,8 @@ void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &
}
}
-void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, float p_ascent) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1877,8 +1943,8 @@ void TextServerAdvanced::font_set_ascent(RID p_font_rid, int p_size, real_t p_as
fd->cache[size]->ascent = p_ascent;
}
-real_t TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1887,14 +1953,14 @@ real_t TextServerAdvanced::font_get_ascent(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->ascent * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->ascent;
}
}
-void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, float p_descent) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -1903,8 +1969,8 @@ void TextServerAdvanced::font_set_descent(RID p_font_rid, int p_size, real_t p_d
fd->cache[size]->descent = p_descent;
}
-real_t TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1913,14 +1979,14 @@ real_t TextServerAdvanced::font_get_descent(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->descent * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->descent;
}
}
-void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size, float p_underline_position) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1930,8 +1996,8 @@ void TextServerAdvanced::font_set_underline_position(RID p_font_rid, int p_size,
fd->cache[size]->underline_position = p_underline_position;
}
-real_t TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1940,14 +2006,14 @@ real_t TextServerAdvanced::font_get_underline_position(RID p_font_rid, int p_siz
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->underline_position * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->underline_position;
}
}
-void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size, float p_underline_thickness) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1957,8 +2023,8 @@ void TextServerAdvanced::font_set_underline_thickness(RID p_font_rid, int p_size
fd->cache[size]->underline_thickness = p_underline_thickness;
}
-real_t TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1967,14 +2033,14 @@ real_t TextServerAdvanced::font_get_underline_thickness(RID p_font_rid, int p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->underline_thickness * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->underline_thickness;
}
}
-void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, float p_scale) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1984,8 +2050,8 @@ void TextServerAdvanced::font_set_scale(RID p_font_rid, int p_size, real_t p_sca
fd->cache[size]->scale = p_scale;
}
-real_t TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+float TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1994,14 +2060,14 @@ real_t TextServerAdvanced::font_get_scale(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->scale * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->scale / fd->cache[size]->oversampling;
}
}
void TextServerAdvanced::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2022,7 +2088,7 @@ void TextServerAdvanced::font_set_spacing(RID p_font_rid, int p_size, TextServer
}
int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -2033,14 +2099,14 @@ int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer:
switch (p_spacing) {
case TextServer::SPACING_GLYPH: {
if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->spacing_glyph * (float)p_size / (float)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 * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->spacing_space * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->spacing_space;
}
@@ -2053,7 +2119,7 @@ int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer:
}
int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -2065,7 +2131,7 @@ int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p
}
void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *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);
@@ -2075,7 +2141,7 @@ void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_s
}
void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2083,11 +2149,11 @@ void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_s
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size());
- fd->cache[size]->textures.remove(p_texture_index);
+ fd->cache[size]->textures.remove_at(p_texture_index);
}
void TextServerAdvanced::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -2113,7 +2179,7 @@ void TextServerAdvanced::font_set_texture_image(RID p_font_rid, const Vector2i &
}
Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -2128,12 +2194,13 @@ Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vect
}
void TextServerAdvanced::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *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);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(p_texture_index < 0);
if (p_texture_index >= fd->cache[size]->textures.size()) {
fd->cache[size]->textures.resize(p_texture_index + 1);
}
@@ -2143,7 +2210,7 @@ void TextServerAdvanced::font_set_texture_offsets(RID p_font_rid, const Vector2i
}
PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -2156,7 +2223,7 @@ PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, co
}
Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2173,7 +2240,7 @@ Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_
}
void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2184,7 +2251,7 @@ void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_siz
}
void TextServerAdvanced::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2195,7 +2262,7 @@ void TextServerAdvanced::font_remove_glyph(RID p_font_rid, const Vector2i &p_siz
}
Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2209,14 +2276,14 @@ Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, i
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].advance;
}
}
void TextServerAdvanced::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2231,7 +2298,7 @@ void TextServerAdvanced::font_set_glyph_advance(RID p_font_rid, int p_size, int3
}
Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2245,14 +2312,14 @@ Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].rect.position * (float)p_size.x / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].rect.position;
}
}
void TextServerAdvanced::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2267,7 +2334,7 @@ void TextServerAdvanced::font_set_glyph_offset(RID p_font_rid, const Vector2i &p
}
Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2281,14 +2348,14 @@ Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].rect.size * (float)p_size.x / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].rect.size;
}
}
void TextServerAdvanced::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2303,7 +2370,7 @@ void TextServerAdvanced::font_set_glyph_size(RID p_font_rid, const Vector2i &p_s
}
Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -2319,7 +2386,7 @@ Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i
}
void TextServerAdvanced::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2334,7 +2401,7 @@ void TextServerAdvanced::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &
}
int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -2350,7 +2417,7 @@ int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2
}
void TextServerAdvanced::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2364,42 +2431,50 @@ void TextServerAdvanced::font_set_glyph_texture_idx(RID p_font_rid, const Vector
gl[p_glyph].found = true;
}
-bool TextServerAdvanced::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
- ERR_FAIL_COND_V(!fd, false);
+Dictionary TextServerAdvanced::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ Vector<Vector3> points;
+ Vector<int32_t> contours;
+ bool orientation;
#ifdef MODULE_FREETYPE_ENABLED
int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- ERR_FAIL_COND_V(error, false);
+ ERR_FAIL_COND_V(error, Dictionary());
- r_points.clear();
- r_contours.clear();
+ points.clear();
+ contours.clear();
- real_t h = fd->cache[size]->ascent;
- real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
+ float h = fd->cache[size]->ascent;
+ float scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
- scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ scale = scale * (float)p_size / (float)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- r_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, h - 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++) {
- r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
}
- r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+ orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
#else
- return false;
+ return Dictionary();
#endif
- return true;
+
+ Dictionary out;
+ out["points"] = points;
+ out["contours"] = contours;
+ out["orientation"] = orientation;
+ return out;
}
Array TextServerAdvanced::font_get_kerning_list(RID p_font_rid, int p_size) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -2415,7 +2490,7 @@ Array TextServerAdvanced::font_get_kerning_list(RID p_font_rid, int p_size) cons
}
void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2426,7 +2501,7 @@ void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) {
}
void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2437,7 +2512,7 @@ void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const V
}
void TextServerAdvanced::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2448,7 +2523,7 @@ void TextServerAdvanced::font_set_kerning(RID p_font_rid, int p_size, const Vect
}
Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -2460,7 +2535,7 @@ Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const V
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
- return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return kern[p_glyph_pair] * (float)p_size / (float)fd->msdf_source_size;
} else {
return kern[p_glyph_pair];
}
@@ -2470,7 +2545,7 @@ Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const V
FT_Vector delta;
FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
if (fd->msdf) {
- return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return Vector2(delta.x, delta.y) * (float)p_size / (float)fd->msdf_source_size;
} else {
return Vector2(delta.x, delta.y);
}
@@ -2481,8 +2556,10 @@ Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const V
}
int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *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) + ".");
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
@@ -2504,8 +2581,9 @@ int32_t TextServerAdvanced::font_get_glyph_index(RID p_font_rid, int p_size, cha
}
bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *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) + ".");
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
@@ -2522,7 +2600,7 @@ bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const {
}
String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -2556,8 +2634,10 @@ String TextServerAdvanced::font_get_supported_chars(RID p_font_rid) const {
}
void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *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) + ".");
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
@@ -2574,7 +2654,7 @@ void TextServerAdvanced::font_render_range(RID p_font_rid, const Vector2i &p_siz
}
void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2584,14 +2664,14 @@ void TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_siz
}
void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ 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));
if (!_ensure_glyph(fd, size, p_index)) {
- return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
}
const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
@@ -2609,13 +2689,13 @@ void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
if (fd->msdf) {
Point2 cpos = p_pos;
- cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
- Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size;
+ cpos += gl.rect.position * (float)p_size / (float)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
} else {
- Point2i cpos = p_pos;
+ Point2 cpos = p_pos.floor();
cpos += gl.rect.position;
- Size2i csize = gl.rect.size;
+ Size2 csize = gl.rect.size;
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
}
}
@@ -2624,14 +2704,14 @@ void TextServerAdvanced::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
}
void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
if (!_ensure_glyph(fd, size, p_index)) {
- return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
}
const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
@@ -2649,13 +2729,13 @@ void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
if (fd->msdf) {
Point2 cpos = p_pos;
- cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
- Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size;
+ cpos += gl.rect.position * (float)p_size / (float)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
} else {
- Point2i cpos = p_pos;
+ Point2 cpos = p_pos.floor();
cpos += gl.rect.position;
- Size2i csize = gl.rect.size;
+ Size2 csize = gl.rect.size;
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
}
}
@@ -2664,7 +2744,7 @@ void TextServerAdvanced::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
}
bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String &p_language) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2676,7 +2756,7 @@ bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String
}
void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2684,7 +2764,7 @@ void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, cons
}
bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2692,7 +2772,7 @@ bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, cons
}
void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2700,19 +2780,19 @@ void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, c
}
Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID p_font_rid) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
MutexLock lock(fd->mutex);
Vector<String> out;
- for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) {
- out.push_back(E->key());
+ for (const KeyValue<String, bool> &E : fd->language_support_overrides) {
+ out.push_back(E.key);
}
return out;
}
bool TextServerAdvanced::font_is_script_supported(RID p_font_rid, const String &p_script) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2721,12 +2801,12 @@ bool TextServerAdvanced::font_is_script_supported(RID p_font_rid, const String &
} else {
Vector2i size = _get_size(fd, 16);
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
- return fd->supported_scripts.has(TS->name_to_tag(p_script));
+ return fd->supported_scripts.has(hb_tag_from_string(p_script.ascii().get_data(), -1));
}
}
void TextServerAdvanced::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2734,7 +2814,7 @@ void TextServerAdvanced::font_set_script_support_override(RID p_font_rid, const
}
bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -2742,7 +2822,7 @@ bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const
}
void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -2750,7 +2830,7 @@ void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, con
}
Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_rid) {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
MutexLock lock(fd->mutex);
@@ -2762,7 +2842,7 @@ Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_
}
Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2772,7 +2852,7 @@ Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const
}
Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) const {
- FontDataAdvanced *fd = font_owner.getornull(p_font_rid);
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -2781,11 +2861,11 @@ Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) con
return fd->supported_varaitions;
}
-real_t TextServerAdvanced::font_get_global_oversampling() const {
+float TextServerAdvanced::font_get_global_oversampling() const {
return oversampling;
}
-void TextServerAdvanced::font_set_global_oversampling(real_t p_oversampling) {
+void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@@ -2803,7 +2883,7 @@ void TextServerAdvanced::font_set_global_oversampling(real_t p_oversampling) {
List<RID> text_bufs;
shaped_owner.get_owned_list(&text_bufs);
for (const RID &E : text_bufs) {
- invalidate(shaped_owner.getornull(E));
+ invalidate(shaped_owner.get_or_null(E));
}
}
}
@@ -2864,11 +2944,11 @@ void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *
}
void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
- ShapedTextDataAdvanced *parent = shaped_owner.getornull(p_shaped->parent);
+ ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = parent->objects.front(); E; E = E->next()) {
- if (E->get().pos >= p_shaped->start && E->get().pos < p_shaped->end) {
- p_shaped->objects[E->key()] = E->get();
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
+ p_shaped->objects[E.key] = E.value;
}
}
@@ -2895,7 +2975,7 @@ RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2910,7 +2990,7 @@ void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
}
void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2924,27 +3004,51 @@ void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Dir
}
TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
MutexLock lock(sd->mutex);
return sd->direction;
}
-void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->custom_punct != p_punct) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->custom_punct = p_punct;
+ invalidate(sd);
+ }
+}
+
+String TextServerAdvanced::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, String());
+ return sd->custom_punct;
+}
+
+void TextServerAdvanced::shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
if (sd->parent != RID()) {
full_copy(sd);
}
- sd->bidi_override = p_override;
+ sd->bidi_override.clear();
+ for (int i = 0; i < p_override.size(); i++) {
+ sd->bidi_override.push_back(p_override[i]);
+ }
invalidate(sd);
}
void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2958,7 +3062,7 @@ void TextServerAdvanced::shaped_text_set_orientation(RID p_shaped, TextServer::O
}
void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2970,7 +3074,7 @@ void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2978,7 +3082,7 @@ bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const {
}
void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2992,7 +3096,7 @@ void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3000,7 +3104,7 @@ bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const {
}
TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
MutexLock lock(sd->mutex);
@@ -3008,13 +3112,13 @@ TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_sh
}
bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_size <= 0, false);
MutexLock lock(sd->mutex);
for (int i = 0; i < p_fonts.size(); i++) {
- ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false);
+ ERR_FAIL_COND_V(!font_owner.get_or_null(p_fonts[i]), false);
}
if (p_text.is_empty()) {
@@ -3041,9 +3145,9 @@ bool TextServerAdvanced::shaped_text_add_string(RID p_shaped, const String &p_te
return true;
}
-bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
+bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
_THREAD_SAFE_METHOD_
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
ERR_FAIL_COND_V(p_key == Variant(), false);
ERR_FAIL_COND_V(sd->objects.has(p_key), false);
@@ -3053,7 +3157,7 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
ShapedTextDataAdvanced::Span span;
- span.start = sd->text.length();
+ span.start = sd->start + sd->text.length();
span.end = span.start + p_length;
span.embedded_key = p_key;
@@ -3071,8 +3175,8 @@ bool TextServerAdvanced::shaped_text_add_object(RID p_shaped, Variant p_key, con
return true;
}
-bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3092,9 +3196,9 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if (E->get().pos == gl.start) {
- key = E->key();
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if (E.value.pos == gl.start) {
+ key = E.key;
break;
}
}
@@ -3123,8 +3227,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
- sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y);
} else {
sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
@@ -3135,66 +3238,66 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
// Align embedded objects to baseline.
- real_t full_ascent = sd->ascent;
- real_t full_descent = sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
@@ -3205,7 +3308,7 @@ bool TextServerAdvanced::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -3220,59 +3323,72 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
ShapedTextDataAdvanced *new_sd = memnew(ShapedTextDataAdvanced);
-
- new_sd->hb_buffer = hb_buffer_create();
new_sd->parent = p_shaped;
new_sd->start = p_start;
new_sd->end = p_start + p_length;
-
new_sd->orientation = sd->orientation;
new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
- new_sd->line_breaks_valid = sd->line_breaks_valid;
- new_sd->justification_ops_valid = sd->justification_ops_valid;
- new_sd->sort_valid = false;
- new_sd->upos = sd->upos;
- new_sd->uthk = sd->uthk;
+
+ if (!_shape_substr(new_sd, sd, p_start, p_length)) {
+ memdelete(new_sd);
+ return RID();
+ }
+ return shaped_owner.make_rid(new_sd);
+}
+
+bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const {
+ if (p_new_sd->valid) {
+ return true;
+ }
+
+ p_new_sd->hb_buffer = hb_buffer_create();
+
+ p_new_sd->line_breaks_valid = p_sd->line_breaks_valid;
+ p_new_sd->justification_ops_valid = p_sd->justification_ops_valid;
+ p_new_sd->sort_valid = false;
+ p_new_sd->upos = p_sd->upos;
+ p_new_sd->uthk = p_sd->uthk;
if (p_length > 0) {
- new_sd->text = sd->text.substr(p_start, p_length);
- new_sd->utf16 = new_sd->text.utf16();
- new_sd->script_iter = memnew(ScriptIterator(new_sd->text, 0, new_sd->text.length()));
+ p_new_sd->text = p_sd->text.substr(p_start - p_sd->start, p_length);
+ p_new_sd->utf16 = p_new_sd->text.utf16();
+ p_new_sd->script_iter = memnew(ScriptIterator(p_new_sd->text, 0, p_new_sd->text.length()));
- int sd_size = sd->glyphs.size();
- const Glyph *sd_glyphs = sd->glyphs.ptr();
- for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
+ int sd_size = p_sd->glyphs.size();
+ const Glyph *sd_glyphs = p_sd->glyphs.ptr();
+ for (int ov = 0; ov < p_sd->bidi_override.size(); ov++) {
UErrorCode err = U_ZERO_ERROR;
- if (sd->bidi_override[ov].x >= p_start + p_length || sd->bidi_override[ov].y <= p_start) {
+ if (p_sd->bidi_override[ov].x >= p_start + p_length || p_sd->bidi_override[ov].y <= p_start) {
continue;
}
- int start = _convert_pos_inv(sd, MAX(0, p_start - sd->bidi_override[ov].x));
- int end = _convert_pos_inv(sd, MIN(p_start + p_length, sd->bidi_override[ov].y) - sd->bidi_override[ov].x);
+ int start = _convert_pos_inv(p_sd, MAX(0, p_start - p_sd->bidi_override[ov].x));
+ int end = _convert_pos_inv(p_sd, MIN(p_start + p_length, p_sd->bidi_override[ov].y) - p_sd->bidi_override[ov].x);
- ERR_FAIL_COND_V_MSG((start < 0 || end - start > new_sd->utf16.length()), RID(), "Invalid BiDi override range.");
+ ERR_FAIL_COND_V_MSG((start < 0 || end - start > p_new_sd->utf16.length()), false, "Invalid BiDi override range.");
// Create temporary line bidi & shape.
UBiDi *bidi_iter = ubidi_openSized(end - start, 0, &err);
- ERR_FAIL_COND_V_MSG(U_FAILURE(err), RID(), u_errorName(err));
- ubidi_setLine(sd->bidi_iter[ov], start, end, bidi_iter, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+ ubidi_setLine(p_sd->bidi_iter[ov], start, end, bidi_iter, &err);
if (U_FAILURE(err)) {
ubidi_close(bidi_iter);
- ERR_FAIL_V_MSG(RID(), u_errorName(err));
+ ERR_FAIL_V_MSG(false, u_errorName(err));
}
- new_sd->bidi_iter.push_back(bidi_iter);
+ p_new_sd->bidi_iter.push_back(bidi_iter);
err = U_ZERO_ERROR;
int bidi_run_count = ubidi_countRuns(bidi_iter, &err);
- ERR_FAIL_COND_V_MSG(U_FAILURE(err), RID(), u_errorName(err));
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
for (int i = 0; i < bidi_run_count; i++) {
int32_t _bidi_run_start = 0;
int32_t _bidi_run_length = 0;
ubidi_getVisualRun(bidi_iter, i, &_bidi_run_start, &_bidi_run_length);
- int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start);
- int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length);
+ int32_t bidi_run_start = _convert_pos(p_sd, p_sd->bidi_override[ov].x + start + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(p_sd, p_sd->bidi_override[ov].x + start + _bidi_run_start + _bidi_run_length);
for (int j = 0; j < sd_size; j++) {
if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
@@ -3281,132 +3397,131 @@ RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_leng
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if (E->get().pos == gl.start) {
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_sd->objects) {
+ if (E.value.pos == gl.start) {
find_embedded = true;
- key = E->key();
- new_sd->objects[key] = E->get();
+ key = E.key;
+ p_new_sd->objects[key] = E.value;
break;
}
}
}
if (find_embedded) {
- if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->objects[key].rect.position.x = new_sd->width;
- new_sd->width += new_sd->objects[key].rect.size.x;
+ if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ p_new_sd->objects[key].rect.position.x = p_new_sd->width;
+ p_new_sd->width += p_new_sd->objects[key].rect.size.x;
} else {
- new_sd->objects[key].rect.position.y = new_sd->width;
- new_sd->width += new_sd->objects[key].rect.size.y;
+ p_new_sd->objects[key].rect.position.y = p_new_sd->width;
+ p_new_sd->width += p_new_sd->objects[key].rect.size.y;
}
} else {
if (gl.font_rid.is_valid()) {
- if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
- new_sd->descent = MAX(new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
+ p_new_sd->descent = MAX(p_new_sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
} else {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
- new_sd->descent = MAX(new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ p_new_sd->descent = MAX(p_new_sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
}
- } else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
+ } else if (p_new_sd->preserve_invalid || (p_new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
- if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
- new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
+ p_new_sd->ascent = MAX(p_new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y);
} else {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
- new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ p_new_sd->descent = MAX(p_new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
}
}
- new_sd->width += gl.advance * gl.repeat;
+ p_new_sd->width += gl.advance * gl.repeat;
}
- new_sd->glyphs.push_back(gl);
+ p_new_sd->glyphs.push_back(gl);
}
}
}
}
// Align embedded objects to baseline.
- real_t full_ascent = new_sd->ascent;
- real_t full_descent = new_sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
- if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
- if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -new_sd->ascent;
+ float full_ascent = p_new_sd->ascent;
+ float full_descent = p_new_sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : p_new_sd->objects) {
+ if ((E.value.pos >= p_new_sd->start) && (E.value.pos < p_new_sd->end)) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -p_new_sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-p_new_sd->ascent + p_new_sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = new_sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = p_new_sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -new_sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -p_new_sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-p_new_sd->ascent + p_new_sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = new_sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = p_new_sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
- new_sd->ascent = full_ascent;
- new_sd->descent = full_descent;
+ p_new_sd->ascent = full_ascent;
+ p_new_sd->descent = full_descent;
}
- new_sd->valid = true;
+ p_new_sd->valid = true;
- return shaped_owner.make_rid(new_sd);
+ return true;
}
RID TextServerAdvanced::shaped_text_get_parent(RID p_shaped) const {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
return sd->parent;
}
-real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3446,7 +3561,7 @@ real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width
}
}
- real_t justification_width;
+ float justification_width;
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
if (sd->overrun_trim_data.trim_pos >= 0) {
start_pos = sd->overrun_trim_data.trim_pos;
@@ -3486,7 +3601,7 @@ real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width
}
if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
- real_t delta_width_per_kashida = (p_width - justification_width) / elongation_count;
+ float 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];
if (gl.count > 0) {
@@ -3501,15 +3616,15 @@ real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width
}
}
}
- real_t adv_remain = 0;
+ float adv_remain = 0;
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
- real_t delta_width_per_space = (p_width - justification_width) / space_count;
+ float 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];
if (gl.count > 0) {
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
- real_t old_adv = gl.advance;
- real_t new_advance;
+ float old_adv = gl.advance;
+ float new_advance;
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
new_advance = MAX(gl.advance + delta_width_per_space, 0.f);
} else {
@@ -3541,8 +3656,8 @@ real_t TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, real_t p_width
return sd->width;
}
-real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3554,7 +3669,7 @@ real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real
}
int tab_index = 0;
- real_t off = 0.f;
+ float off = 0.f;
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
@@ -3571,7 +3686,7 @@ real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real
for (int i = start; i != end; i += delta) {
if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
- real_t tab_off = 0.f;
+ float tab_off = 0.f;
while (tab_off <= off) {
tab_off += p_tab_stops[tab_index];
tab_index++;
@@ -3579,7 +3694,7 @@ real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real
tab_index = 0;
}
}
- real_t old_adv = gl[i].advance;
+ float old_adv = gl[i].advance;
gl[i].advance = tab_off - off;
sd->width += gl[i].advance - old_adv;
off = 0;
@@ -3591,8 +3706,8 @@ real_t TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const Vector<real
return 0.f;
}
-void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped_line);
+void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataAdvanced invalid.");
MutexLock lock(sd->mutex);
@@ -3600,6 +3715,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
shaped_text_shape(p_shaped_line);
}
+ sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
@@ -3633,7 +3749,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
}
int ell_min_characters = 6;
- real_t width = sd->width;
+ float width = sd->width;
bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL);
@@ -3652,10 +3768,10 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
}
if (sd_glyphs[i].count > 0) {
- bool above_min_char_treshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
+ bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
- if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
- if (cut_per_word && above_min_char_treshold) {
+ if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
+ if (cut_per_word && above_min_char_threshold) {
if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
last_valid_cut = i;
found = true;
@@ -3667,7 +3783,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
if (found) {
trim_pos = last_valid_cut;
- if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
ellipsis_pos = trim_pos;
}
break;
@@ -3689,7 +3805,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {
// Insert an additional space when cutting word bound for aesthetics.
if (cut_per_word && (ellipsis_pos > 0)) {
- TextServer::Glyph gl;
+ Glyph gl;
gl.count = 1;
gl.advance = whitespace_adv.x;
gl.index = whitespace_gl_idx;
@@ -3700,7 +3816,7 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
}
// Add ellipsis dots.
- TextServer::Glyph gl;
+ Glyph gl;
gl.count = 1;
gl.repeat = 3;
gl.advance = dot_adv.x;
@@ -3717,16 +3833,40 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
}
}
-TextServer::TrimData TextServerAdvanced::shaped_text_get_trim_data(RID p_shaped) const {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataAdvanced invalid.");
+int TextServerAdvanced::shaped_text_get_trim_pos(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.trim_pos;
+}
+
+int TextServerAdvanced::shaped_text_get_ellipsis_pos(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextDataAdvanced invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.ellipsis_pos;
+}
+
+const Glyph *TextServerAdvanced::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextDataAdvanced invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
+}
+
+int TextServerAdvanced::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextDataAdvanced invalid.");
MutexLock lock(sd->mutex);
- return sd->overrun_trim_data;
+ return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3784,6 +3924,9 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
const char32_t *ch = sd->text.ptr();
Glyph *sd_glyphs = sd->glyphs.ptrw();
+ int c_punct_size = sd->custom_punct.length();
+ const char32_t *c_punct = sd->custom_punct.ptr();
+
for (i = 0; i < sd_size; i++) {
if (sd_glyphs[i].count > 0) {
char32_t c = ch[sd_glyphs[i].start - sd->start];
@@ -3796,12 +3939,21 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
}
+ if (c_punct_size == 0) {
+ if (u_ispunct(c) && c != 0x005F) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ } else {
+ for (int j = 0; j < c_punct_size; j++) {
+ if (c_punct[j] == c) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ break;
+ }
+ }
+ }
if (is_underscore(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
- if (u_ispunct(c) && c != 0x005F) {
- sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
- }
if (breaks.has(sd->glyphs[i].start)) {
if (breaks[sd->glyphs[i].start]) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
@@ -3809,14 +3961,19 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
if (is_whitespace(c)) {
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
} else {
- TextServer::Glyph gl;
+ Glyph gl;
gl.start = sd_glyphs[i].start;
gl.end = sd_glyphs[i].end;
gl.count = 1;
gl.font_rid = sd_glyphs[i].font_rid;
gl.font_size = sd_glyphs[i].font_size;
gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
- sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after.
+ if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) {
+ gl.flags |= GRAPHEME_IS_RTL;
+ sd->glyphs.insert(i, gl); // Insert before.
+ } else {
+ sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after.
+ }
// Update write pointer and size.
sd_size = sd->glyphs.size();
@@ -3913,7 +4070,7 @@ _FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_d
}
bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -3985,7 +4142,7 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
sd->glyphs.write[i].flags |= GRAPHEME_IS_ELONGATION;
} else {
if (sd->glyphs[i].font_rid != RID()) {
- TextServer::Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
+ Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
if ((gl.flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
gl.start = sd->glyphs[i].start;
gl.end = sd->glyphs[i].end;
@@ -4003,14 +4160,19 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
}
}
} else if (!is_whitespace(c)) {
- TextServer::Glyph gl;
+ Glyph gl;
gl.start = sd->glyphs[i].start;
gl.end = sd->glyphs[i].end;
gl.count = 1;
gl.font_rid = sd->glyphs[i].font_rid;
gl.font_size = sd->glyphs[i].font_size;
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL;
- sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after.
+ if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) {
+ gl.flags |= GRAPHEME_IS_RTL;
+ sd->glyphs.insert(i, gl); // Insert before.
+ } else {
+ sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after.
+ }
i += sd->glyphs[i].count;
continue;
}
@@ -4023,9 +4185,9 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
return sd->justification_ops_valid;
}
-TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
+Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size) {
hb_font_t *hb_font = _font_get_hb_handle(p_font, p_font_size);
- ERR_FAIL_COND_V(hb_font == nullptr, TextServer::Glyph());
+ ERR_FAIL_COND_V(hb_font == nullptr, Glyph());
hb_buffer_clear_contents(p_sd->hb_buffer);
hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
@@ -4040,7 +4202,7 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
// Process glyphs.
- TextServer::Glyph gl;
+ Glyph gl;
if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
gl.flags |= TextServer::GRAPHEME_IS_RTL;
@@ -4050,7 +4212,7 @@ TextServer::Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced
gl.font_size = p_font_size;
if (glyph_count > 0) {
- real_t scale = font_get_scale(p_font, p_font_size);
+ float scale = font_get_scale(p_font, p_font_size);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = Math::round(glyph_pos[0].x_advance / (64.0 / scale));
} else {
@@ -4075,9 +4237,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
// Add fallback glyphs.
for (int i = p_start; i < p_end; i++) {
if (p_sd->preserve_invalid || (p_sd->preserve_control && is_control(p_sd->text[i]))) {
- TextServer::Glyph gl;
- gl.start = i;
- gl.end = i + 1;
+ Glyph gl;
+ gl.start = i + p_sd->start;
+ gl.end = i + 1 + p_sd->start;
gl.count = 1;
gl.index = p_sd->text[i];
gl.font_size = fs;
@@ -4087,8 +4249,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
}
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = get_hex_code_box_size(fs, gl.index).x;
- p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.75f));
- p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).y * 0.25f));
+ p_sd->ascent = MAX(p_sd->ascent, get_hex_code_box_size(fs, gl.index).y);
} else {
gl.advance = get_hex_code_box_size(fs, gl.index).y;
p_sd->ascent = MAX(p_sd->ascent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
@@ -4104,6 +4265,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
RID f = p_fonts[p_fb_index];
hb_font_t *hb_font = _font_get_hb_handle(f, fs);
+ float scale = font_get_scale(f, fs);
+ float sp_sp = font_get_spacing(f, fs, SPACING_SPACE);
+ float sp_gl = font_get_spacing(f, fs, SPACING_GLYPH);
ERR_FAIL_COND(hb_font == nullptr);
hb_buffer_clear_contents(p_sd->hb_buffer);
@@ -4115,7 +4279,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
}
hb_buffer_set_script(p_sd->hb_buffer, p_script);
- if (p_sd->spans[p_span].language != String()) {
+ if (!p_sd->spans[p_span].language.is_empty()) {
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);
}
@@ -4142,7 +4306,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
// Process glyphs.
if (glyph_count > 0) {
- TextServer::Glyph *w = (TextServer::Glyph *)memalloc(glyph_count * sizeof(TextServer::Glyph));
+ Glyph *w = (Glyph *)memalloc(glyph_count * sizeof(Glyph));
int end = (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) ? p_end : 0;
uint32_t last_cluster_id = UINT32_MAX;
@@ -4159,7 +4323,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
}
}
if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
- w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL;
+ w[last_cluster_index].flags |= GRAPHEME_IS_RTL;
}
if (last_cluster_valid) {
w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
@@ -4171,8 +4335,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
last_cluster_id = glyph_info[i].cluster;
- TextServer::Glyph &gl = w[i];
- gl = TextServer::Glyph();
+ Glyph &gl = w[i];
+ gl = Glyph();
gl.start = glyph_info[i].cluster;
gl.end = end;
@@ -4181,9 +4345,12 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
gl.font_rid = p_fonts[p_fb_index];
gl.font_size = fs;
+ if (glyph_info[i].mask & HB_GLYPH_FLAG_DEFINED) {
+ gl.flags |= GRAPHEME_IS_CONNECTED;
+ }
+
gl.index = glyph_info[i].codepoint;
if (gl.index != 0) {
- real_t scale = font_get_scale(f, fs);
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = Math::round(glyph_pos[i].x_advance / (64.0 / scale));
} else {
@@ -4192,16 +4359,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
gl.x_off = Math::round(glyph_pos[i].x_offset / (64.0 / scale));
gl.y_off = -Math::round(glyph_pos[i].y_offset / (64.0 / scale));
}
- if (font_get_spacing(f, fs, SPACING_SPACE) && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
- gl.advance += font_get_spacing(f, fs, SPACING_SPACE);
+ if (sp_sp && is_whitespace(p_sd->text[glyph_info[i].cluster])) {
+ gl.advance += sp_sp;
} else {
- gl.advance += font_get_spacing(f, fs, SPACING_GLYPH);
+ gl.advance += sp_gl;
}
if (p_sd->preserve_control) {
- last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || is_whitespace(p_sd->text[glyph_info[i].cluster]) || is_linebreak(p_sd->text[glyph_info[i].cluster]));
+ last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || (p_sd->text[glyph_info[i].cluster] == 0x0009) || (u_isblank(p_sd->text[glyph_info[i].cluster]) && (gl.advance != 0)) || (!u_isblank(p_sd->text[glyph_info[i].cluster]) && is_linebreak(p_sd->text[glyph_info[i].cluster])));
} else {
- last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || !u_isgraph(p_sd->text[glyph_info[i].cluster]));
+ last_cluster_valid = last_cluster_valid && ((glyph_info[i].codepoint != 0) || (p_sd->text[glyph_info[i].cluster] == 0x0009) || (u_isblank(p_sd->text[glyph_info[i].cluster]) && (gl.advance != 0)) || (!u_isblank(p_sd->text[glyph_info[i].cluster]) && !u_isgraph(p_sd->text[glyph_info[i].cluster])));
}
}
if (p_direction == HB_DIRECTION_LTR || p_direction == HB_DIRECTION_TTB) {
@@ -4211,7 +4378,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
}
w[last_cluster_index].count = glyph_count - last_cluster_index;
if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
- w[last_cluster_index].flags |= TextServer::GRAPHEME_IS_RTL;
+ w[last_cluster_index].flags |= GRAPHEME_IS_RTL;
}
if (last_cluster_valid) {
w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
@@ -4233,10 +4400,13 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off);
p_sd->descent = MAX(p_sd->descent, w[i + j].y_off);
} else {
- p_sd->ascent = MAX(p_sd->ascent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5));
- p_sd->descent = MAX(p_sd->descent, Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5));
+ float gla = Math::round(font_get_glyph_advance(f, fs, w[i + j].index).x * 0.5);
+ p_sd->ascent = MAX(p_sd->ascent, gla);
+ p_sd->descent = MAX(p_sd->descent, gla);
}
p_sd->width += w[i + j].advance;
+ w[i + j].start += p_sd->start;
+ w[i + j].end += p_sd->start;
p_sd->glyphs.push_back(w[i + j]);
}
} else {
@@ -4261,7 +4431,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
}
bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -4269,10 +4439,14 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
return true;
}
+ invalidate(sd);
if (sd->parent != RID()) {
- full_copy(sd);
+ shaped_text_shape(sd->parent);
+ ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
+ ERR_FAIL_COND_V(!parent_sd->valid, false);
+ ERR_FAIL_COND_V(!_shape_substr(sd, parent_sd, sd->start, sd->end - sd->start), false);
+ return true;
}
- invalidate(sd);
if (sd->text.length() == 0) {
sd->valid = true;
@@ -4288,15 +4462,17 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
if (sd->bidi_override.is_empty()) {
- sd->bidi_override.push_back(Vector2i(0, sd->end));
+ sd->bidi_override.push_back(Vector2i(sd->start, sd->end));
}
for (int ov = 0; ov < sd->bidi_override.size(); ov++) {
// Create BiDi iterator.
- int start = _convert_pos_inv(sd, sd->bidi_override[ov].x);
- int end = _convert_pos_inv(sd, sd->bidi_override[ov].y);
+ int start = _convert_pos_inv(sd, sd->bidi_override[ov].x - sd->start);
+ int end = _convert_pos_inv(sd, sd->bidi_override[ov].y - sd->start);
- ERR_FAIL_COND_V_MSG((start < 0 || end - start > sd->utf16.length()), false, "Invalid BiDi override range.");
+ if (start < 0 || end - start > sd->utf16.length()) {
+ continue;
+ }
UErrorCode err = U_ZERO_ERROR;
UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
@@ -4350,8 +4526,8 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
}
- int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x + _bidi_run_start);
- int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x + _bidi_run_start + _bidi_run_length);
+ int32_t bidi_run_start = _convert_pos(sd, sd->bidi_override[ov].x - sd->start + _bidi_run_start);
+ int32_t bidi_run_end = _convert_pos(sd, sd->bidi_override[ov].x - sd->start + _bidi_run_start + _bidi_run_length);
// Shape runs.
@@ -4373,7 +4549,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
for (int k = spn_from; k != spn_to; k += spn_delta) {
const ShapedTextDataAdvanced::Span &span = sd->spans[k];
- if (span.start >= script_run_end || span.end <= script_run_start) {
+ if (span.start - sd->start >= script_run_end || span.end - sd->start <= script_run_start) {
continue;
}
if (span.embedded_key != Variant()) {
@@ -4414,7 +4590,7 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
fonts.append_array(fonts_scr_only);
fonts.append_array(fonts_no_match);
- _shape_run(sd, MAX(sd->spans[k].start, script_run_start), MIN(sd->spans[k].end, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
+ _shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0);
}
}
}
@@ -4423,65 +4599,65 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
// Align embedded objects to baseline.
- real_t full_ascent = sd->ascent;
- real_t full_descent = sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
sd->ascent = full_ascent;
@@ -4491,35 +4667,38 @@ bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
}
bool TextServerAdvanced::shaped_text_is_ready(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
return sd->valid;
}
-Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_get_glyphs(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+const Glyph *TextServerAdvanced::shaped_text_get_glyphs(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
}
- return sd->glyphs;
+ return sd->glyphs.ptr();
}
-Vector2i TextServerAdvanced::shaped_text_get_range(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector2i());
+int TextServerAdvanced::shaped_text_get_glyph_count(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
- return Vector2(sd->start, sd->end);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs.size();
}
-Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) {
- ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+const Glyph *TextServerAdvanced::shaped_text_sort_logical(RID p_shaped) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
@@ -4528,28 +4707,36 @@ Vector<TextServer::Glyph> TextServerAdvanced::shaped_text_sort_logical(RID p_sha
if (!sd->sort_valid) {
sd->glyphs_logical = sd->glyphs;
- sd->glyphs_logical.sort_custom<TextServer::GlyphCompare>();
+ sd->glyphs_logical.sort_custom<GlyphCompare>();
sd->sort_valid = true;
}
- return sd->glyphs_logical;
+ return sd->glyphs_logical.ptr();
+}
+
+Vector2i TextServerAdvanced::shaped_text_get_range(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector2i());
+
+ MutexLock lock(sd->mutex);
+ return Vector2(sd->start, sd->end);
}
Array TextServerAdvanced::shaped_text_get_objects(RID p_shaped) const {
Array ret;
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ ret.push_back(E.key);
}
return ret;
}
Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
@@ -4561,7 +4748,7 @@ Rect2 TextServerAdvanced::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
@@ -4575,8 +4762,8 @@ Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
}
}
-real_t TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -4586,8 +4773,8 @@ real_t TextServerAdvanced::shaped_text_get_ascent(RID p_shaped) const {
return sd->ascent;
}
-real_t TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -4597,8 +4784,8 @@ real_t TextServerAdvanced::shaped_text_get_descent(RID p_shaped) const {
return sd->descent;
}
-real_t TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -4608,8 +4795,8 @@ real_t TextServerAdvanced::shaped_text_get_width(RID p_shaped) const {
return (sd->text_trimmed ? sd->width_trimmed : sd->width);
}
-real_t TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -4620,8 +4807,8 @@ real_t TextServerAdvanced::shaped_text_get_underline_position(RID p_shaped) cons
return sd->upos;
}
-real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const {
- const ShapedTextDataAdvanced *sd = shaped_owner.getornull(p_shaped);
+float TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -4632,32 +4819,188 @@ real_t TextServerAdvanced::shaped_text_get_underline_thickness(RID p_shaped) con
return sd->uthk;
}
-struct num_system_data {
- String lang;
- String digits;
- String percent_sign;
- String exp;
-};
+void TextServerAdvanced::_insert_num_systems_lang() {
+ // Eastern Arabic numerals.
+ {
+ NumSystemData ar;
+ ar.lang.insert(StringName("ar")); // Arabic
+ ar.lang.insert(StringName("ar_AE"));
+ ar.lang.insert(StringName("ar_BH"));
+ ar.lang.insert(StringName("ar_DJ"));
+ ar.lang.insert(StringName("ar_EG"));
+ ar.lang.insert(StringName("ar_ER"));
+ ar.lang.insert(StringName("ar_IL"));
+ ar.lang.insert(StringName("ar_IQ"));
+ ar.lang.insert(StringName("ar_JO"));
+ ar.lang.insert(StringName("ar_KM"));
+ ar.lang.insert(StringName("ar_KW"));
+ ar.lang.insert(StringName("ar_LB"));
+ ar.lang.insert(StringName("ar_MR"));
+ ar.lang.insert(StringName("ar_OM"));
+ ar.lang.insert(StringName("ar_PS"));
+ ar.lang.insert(StringName("ar_QA"));
+ ar.lang.insert(StringName("ar_SA"));
+ ar.lang.insert(StringName("ar_SD"));
+ ar.lang.insert(StringName("ar_SO"));
+ ar.lang.insert(StringName("ar_SS"));
+ ar.lang.insert(StringName("ar_SY"));
+ ar.lang.insert(StringName("ar_TD"));
+ ar.lang.insert(StringName("ar_YE"));
+ ar.lang.insert(StringName("ckb")); // Central Kurdish
+ ar.lang.insert(StringName("ckb_IQ"));
+ ar.lang.insert(StringName("ckb_IR"));
+ ar.lang.insert(StringName("sd")); // Sindhi
+ ar.lang.insert(StringName("sd_PK"));
+ ar.lang.insert(StringName("sd_Arab"));
+ ar.lang.insert(StringName("sd_Arab_PK"));
+ ar.digits = U"٠١٢٣٤٥٦٧٨٩٫";
+ ar.percent_sign = U"٪";
+ ar.exp = U"اس";
+ num_systems.push_back(ar);
+ }
+
+ // Persian and Urdu numerals.
+ {
+ NumSystemData pr;
+ pr.lang.insert(StringName("fa")); // Persian
+ pr.lang.insert(StringName("fa_AF"));
+ pr.lang.insert(StringName("fa_IR"));
+ pr.lang.insert(StringName("ks")); // Kashmiri
+ pr.lang.insert(StringName("ks_IN"));
+ pr.lang.insert(StringName("ks_Arab"));
+ pr.lang.insert(StringName("ks_Arab_IN"));
+ pr.lang.insert(StringName("lrc")); // Northern Luri
+ pr.lang.insert(StringName("lrc_IQ"));
+ pr.lang.insert(StringName("lrc_IR"));
+ pr.lang.insert(StringName("mzn")); // Mazanderani
+ pr.lang.insert(StringName("mzn_IR"));
+ pr.lang.insert(StringName("pa_PK")); // Panjabi
+ pr.lang.insert(StringName("pa_Arab"));
+ pr.lang.insert(StringName("pa_Arab_PK"));
+ pr.lang.insert(StringName("ps")); // Pushto
+ pr.lang.insert(StringName("ps_AF"));
+ pr.lang.insert(StringName("ps_PK"));
+ pr.lang.insert(StringName("ur_IN")); // Urdu
+ pr.lang.insert(StringName("uz_AF")); // Uzbek
+ pr.lang.insert(StringName("uz_Arab"));
+ pr.lang.insert(StringName("uz_Arab_AF"));
+ pr.digits = U"۰۱۲۳۴۵۶۷۸۹٫";
+ pr.percent_sign = U"٪";
+ pr.exp = U"اس";
+ num_systems.push_back(pr);
+ }
+
+ // Bengali numerals.
+ {
+ NumSystemData bn;
+ bn.lang.insert(StringName("as")); // Assamese
+ bn.lang.insert(StringName("as_IN"));
+ bn.lang.insert(StringName("bn")); // Bengali
+ bn.lang.insert(StringName("bn_BD"));
+ bn.lang.insert(StringName("bn_IN"));
+ bn.lang.insert(StringName("mni")); // Manipuri
+ bn.lang.insert(StringName("mni_IN"));
+ bn.lang.insert(StringName("mni_Beng"));
+ bn.lang.insert(StringName("mni_Beng_IN"));
+ bn.digits = U"০১২৩৪৫৬৭৮৯.";
+ bn.percent_sign = U"%";
+ bn.exp = U"e";
+ num_systems.push_back(bn);
+ }
+
+ // Devanagari numerals.
+ {
+ NumSystemData mr;
+ mr.lang.insert(StringName("mr")); // Marathi
+ mr.lang.insert(StringName("mr_IN"));
+ mr.lang.insert(StringName("ne")); // Nepali
+ mr.lang.insert(StringName("ne_IN"));
+ mr.lang.insert(StringName("ne_NP"));
+ mr.lang.insert(StringName("sa")); // Sanskrit
+ mr.lang.insert(StringName("sa_IN"));
+ mr.digits = U"०१२३४५६७८९.";
+ mr.percent_sign = U"%";
+ mr.exp = U"e";
+ num_systems.push_back(mr);
+ }
+
+ // Dzongkha numerals.
+ {
+ NumSystemData dz;
+ dz.lang.insert(StringName("dz")); // Dzongkha
+ dz.lang.insert(StringName("dz_BT"));
+ dz.digits = U"༠༡༢༣༤༥༦༧༨༩.";
+ dz.percent_sign = U"%";
+ dz.exp = U"e";
+ num_systems.push_back(dz);
+ }
-static num_system_data num_systems[]{
- { "ar,ar_AR,ar_BH,ar_DJ,ar_EG,ar_ER,ar_IL,ar_IQ,ar_JO,ar_KM,ar_KW,ar_LB,ar_MR,ar_OM,ar_PS,ar_QA,ar_SA,ar_SD,ar_SO,ar_SS,ar_SY,ar_TD,ar_YE", U"٠١٢٣٤٥٦٧٨٩٫", U"٪", U"اس" },
- { "fa,ks,pa_Arab,ps,ug,ur_IN,ur,uz_Arab", U"۰۱۲۳۴۵۶۷۸۹٫", U"٪", U"اس" },
- { "as,bn,mni", U"০১২৩৪৫৬৭৮৯.", U"%", U"e" },
- { "mr,ne", U"०१२३४५६७८९.", U"%", U"e" },
- { "dz", U"༠༡༢༣༤༥༦༧༨༩.", U"%", U"e" },
- { "sat", U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.", U"%", U"e" },
- { "my", U"၀၁၂၃၄၅၆၇၈၉.", U"%", U"e" },
- { String(), String(), String(), String() },
-};
+ // Santali numerals.
+ {
+ NumSystemData sat;
+ sat.lang.insert(StringName("sat")); // Santali
+ sat.lang.insert(StringName("sat_IN"));
+ sat.lang.insert(StringName("sat_Olck"));
+ sat.lang.insert(StringName("sat_Olck_IN"));
+ sat.digits = U"᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙.";
+ sat.percent_sign = U"%";
+ sat.exp = U"e";
+ num_systems.push_back(sat);
+ }
+
+ // Burmese numerals.
+ {
+ NumSystemData my;
+ my.lang.insert(StringName("my")); // Burmese
+ my.lang.insert(StringName("my_MM"));
+ my.digits = U"၀၁၂၃၄၅၆၇၈၉.";
+ my.percent_sign = U"%";
+ my.exp = U"e";
+ num_systems.push_back(my);
+ }
+
+ // Chakma numerals.
+ {
+ NumSystemData ccp;
+ ccp.lang.insert(StringName("ccp")); // Chakma
+ ccp.lang.insert(StringName("ccp_BD"));
+ ccp.lang.insert(StringName("ccp_IN"));
+ ccp.digits = U"𑄶𑄷𑄸𑄹𑄺𑄻𑄼𑄽𑄾𑄿.";
+ ccp.percent_sign = U"%";
+ ccp.exp = U"e";
+ num_systems.push_back(ccp);
+ }
+
+ // Adlam numerals.
+ {
+ NumSystemData ff;
+ ff.lang.insert(StringName("ff")); // Fulah
+ ff.lang.insert(StringName("ff_Adlm_BF"));
+ ff.lang.insert(StringName("ff_Adlm_CM"));
+ ff.lang.insert(StringName("ff_Adlm_GH"));
+ ff.lang.insert(StringName("ff_Adlm_GM"));
+ ff.lang.insert(StringName("ff_Adlm_GN"));
+ ff.lang.insert(StringName("ff_Adlm_GW"));
+ ff.lang.insert(StringName("ff_Adlm_LR"));
+ ff.lang.insert(StringName("ff_Adlm_MR"));
+ ff.lang.insert(StringName("ff_Adlm_NE"));
+ ff.lang.insert(StringName("ff_Adlm_NG"));
+ ff.lang.insert(StringName("ff_Adlm_SL"));
+ ff.lang.insert(StringName("ff_Adlm_SN"));
+ ff.digits = U"𞥐𞥑𞥒𞥓𞥔𞥕𞥖𞥗𞥘𞥙.";
+ ff.percent_sign = U"%";
+ ff.exp = U"e";
+ num_systems.push_back(ff);
+ }
+}
String TextServerAdvanced::format_number(const String &p_string, const String &p_language) const {
- String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+ const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
- for (int i = 0; num_systems[i].lang != String(); i++) {
- Vector<String> langs = num_systems[i].lang.split(",");
- if (langs.has(lang)) {
- if (num_systems[i].digits == String()) {
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].digits.is_empty()) {
return p_string;
}
res.replace("e", num_systems[i].exp);
@@ -4677,13 +5020,12 @@ String TextServerAdvanced::format_number(const String &p_string, const String &p
}
String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
- String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+ const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
String res = p_string;
- for (int i = 0; num_systems[i].lang != String(); i++) {
- Vector<String> langs = num_systems[i].lang.split(",");
- if (langs.has(lang)) {
- if (num_systems[i].digits == String()) {
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].digits.is_empty()) {
return p_string;
}
res.replace(num_systems[i].exp, "e");
@@ -4706,12 +5048,11 @@ String TextServerAdvanced::parse_number(const String &p_string, const String &p_
}
String TextServerAdvanced::percent_sign(const String &p_language) const {
- String lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+ const StringName lang = (p_language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
- for (int i = 0; num_systems[i].lang != String(); i++) {
- Vector<String> langs = num_systems[i].lang.split(",");
- if (langs.has(lang)) {
- if (num_systems[i].percent_sign == String()) {
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].percent_sign.is_empty()) {
return "%";
}
return num_systems[i].percent_sign;
@@ -4720,21 +5061,47 @@ String TextServerAdvanced::percent_sign(const String &p_language) const {
return "%";
}
-TextServer *TextServerAdvanced::create_func(Error &r_error, void *p_user_data) {
- r_error = OK;
- return memnew(TextServerAdvanced());
-}
+String TextServerAdvanced::strip_diacritics(const String &p_string) const {
+ UErrorCode err = U_ZERO_ERROR;
-void TextServerAdvanced::register_server() {
- TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr);
+ // Get NFKD normalizer singleton.
+ const UNormalizer2 *unorm = unorm2_getNFKDInstance(&err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), TextServer::strip_diacritics(p_string), u_errorName(err));
+
+ // Convert to UTF-16.
+ Char16String utf16 = p_string.utf16();
+
+ // Normalize.
+ Char16String normalized;
+ err = U_ZERO_ERROR;
+ int32_t len = unorm2_normalize(unorm, utf16.ptr(), -1, nullptr, 0, &err);
+ ERR_FAIL_COND_V_MSG(err != U_BUFFER_OVERFLOW_ERROR, TextServer::strip_diacritics(p_string), u_errorName(err));
+ normalized.resize(len);
+ err = U_ZERO_ERROR;
+ unorm2_normalize(unorm, utf16.ptr(), -1, normalized.ptrw(), len, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), TextServer::strip_diacritics(p_string), u_errorName(err));
+
+ // Convert back to UTF-32.
+ String normalized_string = String::utf16(normalized.ptr(), len);
+
+ // Strip combining characters.
+ String result;
+ for (int i = 0; i < normalized_string.length(); i++) {
+ if (u_getCombiningClass(normalized_string[i]) == 0) {
+ result += normalized_string[i];
+ }
+ }
+ return result;
}
TextServerAdvanced::TextServerAdvanced() {
- hb_bmp_create_font_funcs();
+ _insert_num_systems_lang();
+ _insert_feature_sets();
+ _bmp_create_font_funcs();
}
TextServerAdvanced::~TextServerAdvanced() {
- hb_bmp_free_font_funcs();
+ _bmp_free_font_funcs();
if (library != nullptr) {
FT_Done_FreeType(library);
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 5989035800..9a22180f39 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -50,11 +50,12 @@
#include <unicode/udata.h>
#include <unicode/uiter.h>
#include <unicode/uloc.h>
+#include <unicode/unorm2.h>
#include <unicode/uscript.h>
#include <unicode/ustring.h>
#include <unicode/utypes.h>
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
#ifdef MODULE_FREETYPE_ENABLED
#include <ft2build.h>
@@ -79,6 +80,19 @@ class TextServerAdvanced : public TextServer {
static String interface_name;
static uint32_t interface_features;
+ struct NumSystemData {
+ Set<StringName> lang;
+ String digits;
+ String percent_sign;
+ String exp;
+ };
+
+ Vector<NumSystemData> num_systems;
+ Map<StringName, int32_t> feature_sets;
+
+ void _insert_num_systems_lang();
+ void _insert_feature_sets();
+
// ICU support data.
uint8_t *icu_data = nullptr;
@@ -115,12 +129,12 @@ class TextServerAdvanced : public TextServer {
};
struct FontDataForSizeAdvanced {
- real_t ascent = 0.f;
- real_t descent = 0.f;
- real_t underline_position = 0.f;
- real_t underline_thickness = 0.f;
- real_t scale = 1.f;
- real_t oversampling = 1.f;
+ float ascent = 0.f;
+ float descent = 0.f;
+ float underline_position = 0.f;
+ float underline_thickness = 0.f;
+ float scale = 1.f;
+ float oversampling = 1.f;
int spacing_glyph = 0;
int spacing_space = 0;
@@ -161,7 +175,11 @@ class TextServerAdvanced : public TextServer {
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
Dictionary variation_coordinates;
- real_t oversampling = 0.f;
+ float oversampling = 0.f;
+
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
Map<Vector2i, FontDataForSizeAdvanced *> cache;
@@ -245,38 +263,38 @@ class TextServerAdvanced : public TextServer {
// Common data.
- real_t oversampling = 1.f;
+ float oversampling = 1.f;
mutable RID_PtrOwner<FontDataAdvanced> font_owner;
mutable RID_PtrOwner<ShapedTextDataAdvanced> shaped_owner;
int _convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
int _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const;
+ bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const;
void _shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index);
- TextServer::Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size);
+ Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size);
// HarfBuzz bitmap font interface.
static hb_font_funcs_t *funcs;
- struct hb_bmp_font_t {
+ struct bmp_font_t {
TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr;
bool unref = false; /* Whether to destroy bm_face when done. */
};
- static hb_bmp_font_t *_hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
- static void _hb_bmp_font_destroy(void *p_data);
- static hb_bool_t hb_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 hb_bmp_get_glyph_h_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
- static hb_position_t hb_bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
- static hb_position_t hb_bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data);
- static hb_position_t hb_bmp_get_glyph_v_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data);
- static hb_bool_t hb_bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data);
- static hb_bool_t hb_bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data);
- static hb_bool_t hb_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 hb_bmp_create_font_funcs();
- static void hb_bmp_free_font_funcs();
- static void _hb_bmp_font_set_funcs(hb_font_t *p_font, TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
- static hb_font_t *hb_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, hb_destroy_func_t p_destroy);
+ static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *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);
+ static hb_position_t _bmp_get_glyph_v_advance(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, void *p_user_data);
+ static hb_position_t _bmp_get_glyph_h_kerning(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_left_glyph, hb_codepoint_t p_right_glyph, void *p_user_data);
+ static hb_bool_t _bmp_get_glyph_v_origin(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_position_t *r_x, hb_position_t *r_y, void *p_user_data);
+ static hb_bool_t _bmp_get_glyph_extents(hb_font_t *p_font, void *p_font_data, hb_codepoint_t p_glyph, hb_glyph_extents_t *r_extents, void *p_user_data);
+ 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);
protected:
static void _bind_methods(){};
@@ -285,20 +303,19 @@ protected:
void invalidate(ShapedTextDataAdvanced *p_shaped);
public:
- virtual bool has_feature(Feature p_feature) override;
+ virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
+ virtual uint32_t get_features() const override;
virtual void free(RID p_rid) override;
virtual bool has(RID p_rid) override;
virtual bool load_support_data(const String &p_filename) override;
-#ifdef TOOLS_ENABLED
- virtual String get_support_data_filename() override { return _MKSTR(ICU_DATA_NAME); };
- virtual String get_support_data_info() override { return String("ICU break iteration data (") + _MKSTR(ICU_DATA_NAME) + String(")."); };
- virtual bool save_support_data(const String &p_filename) override;
-#endif
+ virtual String get_support_data_filename() const override;
+ virtual String get_support_data_info() const override;
+ virtual bool save_support_data(const String &p_filename) const override;
- virtual bool is_locale_right_to_left(const String &p_locale) override;
+ virtual bool is_locale_right_to_left(const String &p_locale) const override;
virtual int32_t name_to_tag(const String &p_name) const override;
virtual String tag_to_name(int32_t p_tag) const override;
@@ -309,6 +326,15 @@ public:
virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override;
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_style_name(RID p_font_rid) const override;
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_name(RID p_font_rid) const override;
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
virtual bool font_is_antialiased(RID p_font_rid) const override;
@@ -333,8 +359,8 @@ public:
virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
- virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
- virtual real_t font_get_oversampling(RID p_font_rid) const override;
+ virtual void font_set_oversampling(RID p_font_rid, float p_oversampling) override;
+ virtual float font_get_oversampling(RID p_font_rid) const override;
virtual Array font_get_size_cache_list(RID p_font_rid) const override;
virtual void font_clear_size_cache(RID p_font_rid) override;
@@ -342,20 +368,20 @@ public:
hb_font_t *_font_get_hb_handle(RID p_font, int p_font_size) const;
- virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
- virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
+ virtual void font_set_ascent(RID p_font_rid, int p_size, float p_ascent) override;
+ virtual float font_get_ascent(RID p_font_rid, int p_size) const override;
- virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
- virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
+ virtual void font_set_descent(RID p_font_rid, int p_size, float p_descent) override;
+ virtual float font_get_descent(RID p_font_rid, int p_size) const override;
- virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
- virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, float p_underline_position) override;
+ virtual float font_get_underline_position(RID p_font_rid, int p_size) const override;
- virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
- virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, float p_underline_thickness) override;
+ virtual float font_get_underline_thickness(RID p_font_rid, int p_size) const override;
- virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
- virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
+ virtual void font_set_scale(RID p_font_rid, int p_size, float p_scale) override;
+ virtual float font_get_scale(RID p_font_rid, int p_size) const override;
virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
@@ -389,7 +415,7 @@ public:
virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+ virtual Dictionary font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const override;
virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
@@ -424,8 +450,8 @@ public:
virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
- virtual real_t font_get_global_oversampling() const override;
- virtual void font_set_global_oversampling(real_t p_oversampling) override;
+ virtual float font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(float p_oversampling) override;
/* Shaped text buffer interface */
@@ -436,7 +462,10 @@ public:
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
- virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
+ virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
+
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
@@ -448,46 +477,49 @@ public:
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
- virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override;
- virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
- virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
+ virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual float shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
- virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override;
+ virtual int shaped_text_get_trim_pos(RID p_shaped) const override;
+ virtual int shaped_text_get_ellipsis_pos(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_get_ellipsis_glyphs(RID p_shaped) const override;
+ virtual int shaped_text_get_ellipsis_glyph_count(RID p_shaped) const override;
+
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint16_t p_trim_flags) override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
- virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_get_glyphs(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_sort_logical(RID p_shaped) override;
+ virtual int shaped_text_get_glyph_count(RID p_shaped) const override;
virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
- virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
-
virtual Array shaped_text_get_objects(RID p_shaped) const override;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
- virtual real_t shaped_text_get_descent(RID p_shaped) const override;
- virtual real_t shaped_text_get_width(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
+ virtual float shaped_text_get_ascent(RID p_shaped) const override;
+ virtual float shaped_text_get_descent(RID p_shaped) const override;
+ virtual float shaped_text_get_width(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
virtual String format_number(const String &p_string, const String &p_language = "") const override;
virtual String parse_number(const String &p_string, const String &p_language = "") const override;
virtual String percent_sign(const String &p_language = "") const override;
- static TextServer *create_func(Error &r_error, void *p_user_data);
- static void register_server();
+ virtual String strip_diacritics(const String &p_string) const override;
TextServerAdvanced();
~TextServerAdvanced();
diff --git a/modules/text_server_fb/config.py b/modules/text_server_fb/config.py
index 7a73080ae9..275c2b4d53 100644
--- a/modules/text_server_fb/config.py
+++ b/modules/text_server_fb/config.py
@@ -9,3 +9,13 @@ def configure(env):
def is_enabled():
# The module is disabled by default. Use module_text_server_fb_enabled=yes to enable it.
return False
+
+
+def get_doc_classes():
+ return [
+ "TextServerFallback",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/text_server_fb/doc_classes/TextServerFallback.xml b/modules/text_server_fb/doc_classes/TextServerFallback.xml
new file mode 100644
index 0000000000..8aadf2b882
--- /dev/null
+++ b/modules/text_server_fb/doc_classes/TextServerFallback.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextServerFallback" inherits="TextServer" version="4.0">
+ <brief_description>
+ Fallback implementation of the Text Server, without BiDi and complex text layout support.
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+</class>
diff --git a/modules/text_server_fb/register_types.cpp b/modules/text_server_fb/register_types.cpp
index 87cbd2ac2c..0b59040ce8 100644
--- a/modules/text_server_fb/register_types.cpp
+++ b/modules/text_server_fb/register_types.cpp
@@ -33,7 +33,12 @@
#include "text_server_fb.h"
void preregister_text_server_fb_types() {
- TextServerFallback::register_server();
+ GDREGISTER_CLASS(TextServerFallback);
+ if (TextServerManager::get_singleton()) {
+ Ref<TextServerFallback> ts;
+ ts.instantiate();
+ TextServerManager::get_singleton()->add_interface(ts);
+ }
}
void register_text_server_fb_types() {
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index e4e6797f92..8979d9ff4c 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -30,8 +30,11 @@
#include "text_server_fb.h"
+#include "core/error/error_macros.h"
#include "core/string/print_string.h"
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+
#ifdef MODULE_MSDFGEN_ENABLED
#include "core/ShapeDistanceFinder.h"
#include "core/contour-combiners.h"
@@ -68,7 +71,7 @@ _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
String TextServerFallback::interface_name = "Fallback";
uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
-bool TextServerFallback::has_feature(Feature p_feature) {
+bool TextServerFallback::has_feature(Feature p_feature) const {
return (interface_features & p_feature) == p_feature;
}
@@ -76,14 +79,18 @@ String TextServerFallback::get_name() const {
return interface_name;
}
+uint32_t TextServerFallback::get_features() const {
+ return interface_features;
+}
+
void TextServerFallback::free(RID p_rid) {
_THREAD_SAFE_METHOD_
if (font_owner.owns(p_rid)) {
- FontDataFallback *fd = font_owner.getornull(p_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_rid);
font_owner.free(p_rid);
memdelete(fd);
} else if (shaped_owner.owns(p_rid)) {
- ShapedTextData *sd = shaped_owner.getornull(p_rid);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_rid);
shaped_owner.free(p_rid);
memdelete(sd);
}
@@ -98,34 +105,22 @@ bool TextServerFallback::load_support_data(const String &p_filename) {
return false; // No extra data used.
}
-#ifdef TOOLS_ENABLED
-
-bool TextServerFallback::save_support_data(const String &p_filename) {
+bool TextServerFallback::save_support_data(const String &p_filename) const {
return false; // No extra data used.
}
-#endif
-
-bool TextServerFallback::is_locale_right_to_left(const String &p_locale) {
+bool TextServerFallback::is_locale_right_to_left(const String &p_locale) const {
return false; // No RTL support.
}
-#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xFF) << 24) | (((uint32_t)(c2)&0xFF) << 16) | (((uint32_t)(c3)&0xFF) << 8) | ((uint32_t)(c4)&0xFF)))
-
-struct FeatureInfo {
- int32_t tag;
- String name;
-};
-
-static FeatureInfo feature_set[] = {
- // Registered OpenType variation tags.
- { OT_TAG('i', 't', 'a', 'l'), "italic" },
- { OT_TAG('o', 'p', 's', 'z'), "optical_size" },
- { OT_TAG('s', 'l', 'n', 't'), "slant" },
- { OT_TAG('w', 'd', 't', 'h'), "width" },
- { OT_TAG('w', 'g', 'h', 't'), "weight" },
- { 0, String() },
-};
+void TextServerFallback::_insert_feature_sets() {
+ // Registered OpenType variation tag.
+ feature_sets.insert("italic", OT_TAG('i', 't', 'a', 'l'));
+ feature_sets.insert("optical_size", OT_TAG('o', 'p', 's', 'z'));
+ feature_sets.insert("slant", OT_TAG('s', 'l', 'n', 't'));
+ feature_sets.insert("width", OT_TAG('w', 'd', 't', 'h'));
+ feature_sets.insert("weight", OT_TAG('w', 'g', 'h', 't'));
+}
_FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
char tag[4];
@@ -149,10 +144,8 @@ _FORCE_INLINE_ int32_t ot_tag_from_string(const char *p_str, int p_len) {
}
int32_t TextServerFallback::name_to_tag(const String &p_name) const {
- for (int i = 0; feature_set[i].tag != 0; i++) {
- if (feature_set[i].name == p_name) {
- return feature_set[i].tag;
- }
+ if (feature_sets.has(p_name)) {
+ return feature_sets[p_name];
}
// No readable name, use tag string.
@@ -167,9 +160,9 @@ _FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {
}
String TextServerFallback::tag_to_name(int32_t p_tag) const {
- for (int i = 0; feature_set[i].tag != 0; i++) {
- if (feature_set[i].tag == p_tag) {
- return feature_set[i].name;
+ for (const KeyValue<StringName, int32_t> &E : feature_sets) {
+ if (E.value == p_tag) {
+ return E.key;
}
}
@@ -204,6 +197,10 @@ _FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_
continue;
}
+ if (ct.offsets.size() < ct.texture_w) {
+ continue;
+ }
+
ret.y = 0x7FFFFFFF;
ret.x = 0;
@@ -402,7 +399,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
FontGlyph chr;
chr.found = true;
- chr.advance = advance.round();
+ chr.advance = advance;
if (shape.validate() && shape.contours.size() > 0) {
int w = (bounds.r - bounds.l);
@@ -419,8 +416,7 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(
FontTexture &tex = p_data->textures.write[tex_pos.index];
edgeColoringSimple(shape, 3.0); // Max. angle.
- msdfgen::Bitmap<real_t, 4> image(w, h); // Texture size.
- //msdfgen::generateMTSDF(image, shape, p_pixel_range, 1.0, msdfgen::Vector2(-bounds.l, -bounds.b)); // Range, scale, translation.
+ msdfgen::Bitmap<float, 4> image(w, h); // Texture size.
DistancePixelConversion distancePixelConversion(p_pixel_range);
msdfgen::Projection projection(msdfgen::Vector2(1.0, 1.0), msdfgen::Vector2(-bounds.l, -bounds.b));
@@ -514,11 +510,11 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
case FT_PIXEL_MODE_MONO: {
int byte = i * bitmap.pitch + (j >> 3);
int bit = 1 << (7 - (j % 8));
- wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
} break;
case FT_PIXEL_MODE_GRAY:
- wr[ofs + 0] = 255; //grayscale as 1
+ wr[ofs + 0] = 255; // grayscale as 1
wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
break;
case FT_PIXEL_MODE_BGRA: {
@@ -556,12 +552,12 @@ _FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitma
}
FontGlyph chr;
- chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ chr.advance = advance * p_data->scale / p_data->oversampling;
chr.texture_idx = tex_pos.index;
chr.found = true;
chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
- chr.rect.position = (Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling).round();
+ chr.rect.position = Vector2(xofs, -yofs) * p_data->scale / p_data->oversampling;
chr.rect.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
return chr;
}
@@ -610,14 +606,16 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
flags |= FT_LOAD_COLOR;
}
+ int32_t glyph_index = FT_Get_Char_Index(fd->face, p_glyph);
+
FT_Fixed v, h;
- FT_Get_Advance(fd->face, p_glyph, flags, &h);
- FT_Get_Advance(fd->face, p_glyph, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
+ FT_Get_Advance(fd->face, glyph_index, flags, &h);
+ FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
- int error = FT_Load_Glyph(fd->face, p_glyph, flags);
+ int error = FT_Load_Glyph(fd->face, glyph_index, flags);
if (error) {
fd->glyph_map[p_glyph] = FontGlyph();
- ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph.");
+ return false;
}
if (!outline) {
@@ -674,19 +672,20 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_d
}
_FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ ERR_FAIL_COND_V(p_size.x <= 0, false);
if (p_font_data->cache.has(p_size)) {
return true;
}
FontDataForSizeFallback *fd = memnew(FontDataForSizeFallback);
fd->size = p_size;
- if (p_font_data->data_ptr) {
+ if (p_font_data->data_ptr && (p_font_data->data_size > 0)) {
// Init dynamic font.
#ifdef MODULE_FREETYPE_ENABLED
int error = 0;
if (!library) {
error = FT_Init_FreeType(&library);
- ERR_FAIL_COND_V_MSG(error != 0, false, TTR("FreeType: Error initializing library:") + " '" + String(FT_Error_String(error)) + "'.");
+ ERR_FAIL_COND_V_MSG(error != 0, false, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
}
memset(&fd->stream, 0, sizeof(FT_StreamRec));
@@ -704,14 +703,14 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
if (error) {
FT_Done_Face(fd->face);
fd->face = nullptr;
- ERR_FAIL_V_MSG(false, TTR("FreeType: Error loading font:") + " '" + String(FT_Error_String(error)) + "'.");
+ ERR_FAIL_V_MSG(false, "FreeType: Error loading font: '" + String(FT_Error_String(error)) + "'.");
}
if (p_font_data->msdf) {
fd->oversampling = 1.0f;
fd->size.x = p_font_data->msdf_source_size;
} else if (p_font_data->oversampling <= 0.0f) {
- fd->oversampling = TS->font_get_global_oversampling();
+ fd->oversampling = font_get_global_oversampling();
} else {
fd->oversampling = p_font_data->oversampling;
}
@@ -719,18 +718,19 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
if (FT_HAS_COLOR(fd->face) && fd->face->num_fixed_sizes > 0) {
int best_match = 0;
int diff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[0].width));
- fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
+ fd->scale = float(fd->size.x * fd->oversampling) / fd->face->available_sizes[0].width;
for (int i = 1; i < fd->face->num_fixed_sizes; i++) {
int ndiff = ABS(fd->size.x - ((int64_t)fd->face->available_sizes[i].width));
if (ndiff < diff) {
best_match = i;
diff = ndiff;
- fd->scale = real_t(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
+ fd->scale = float(fd->size.x * fd->oversampling) / fd->face->available_sizes[i].width;
}
}
FT_Select_Size(fd->face, best_match);
} else {
- FT_Set_Pixel_Sizes(fd->face, 0, fd->size.x * fd->oversampling);
+ FT_Set_Pixel_Sizes(fd->face, 0, Math::round(fd->size.x * fd->oversampling));
+ fd->scale = ((float)fd->size.x * fd->oversampling) / (float)fd->face->size->metrics.y_ppem;
}
fd->ascent = (fd->face->size->metrics.ascender / 64.0) / fd->oversampling * fd->scale;
@@ -739,6 +739,23 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
fd->underline_thickness = (FT_MulFix(fd->face->underline_thickness, fd->face->size->metrics.y_scale) / 64.0) / fd->oversampling * fd->scale;
if (!p_font_data->face_init) {
+ // Get style flags and name.
+ if (fd->face->family_name != nullptr) {
+ p_font_data->font_name = String::utf8((const char *)fd->face->family_name);
+ }
+ if (fd->face->style_name != nullptr) {
+ p_font_data->style_name = String::utf8((const char *)fd->face->style_name);
+ }
+ p_font_data->style_flags = 0;
+ if (fd->face->style_flags & FT_STYLE_FLAG_BOLD) {
+ p_font_data->style_flags |= FONT_BOLD;
+ }
+ if (fd->face->style_flags & FT_STYLE_FLAG_ITALIC) {
+ p_font_data->style_flags |= FONT_ITALIC;
+ }
+ if (fd->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) {
+ p_font_data->style_flags |= FONT_FIXED_WIDTH;
+ }
// Read OpenType variations.
p_font_data->supported_varaitions.clear();
if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
@@ -766,7 +783,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
for (FT_UInt i = 0; i < amaster->num_axis; i++) {
// Reset to default.
int32_t var_tag = amaster->axis[i].tag;
- real_t var_value = (double)amaster->axis[i].def / 65536.f;
+ float var_value = (double)amaster->axis[i].def / 65536.f;
coords.write[i] = amaster->axis[i].def;
if (p_font_data->variation_coordinates.has(var_tag)) {
@@ -784,7 +801,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
FT_Done_MM_Var(library, amaster);
}
#else
- ERR_FAIL_V_MSG(false, TTR("FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
+ ERR_FAIL_V_MSG(false, "FreeType: Can't load dynamic font, engine is compiled without FreeType support!");
#endif
}
p_font_data->cache[p_size] = fd;
@@ -792,8 +809,8 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_cache_for_size(FontDataFallback
}
_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) {
- for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = p_font_data->cache.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : p_font_data->cache) {
+ memdelete(E.value);
}
p_font_data->cache.clear();
@@ -808,7 +825,7 @@ RID TextServerFallback::create_font() {
}
void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -819,7 +836,7 @@ void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_
}
void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -829,8 +846,68 @@ void TextServerFallback::font_set_data_ptr(RID p_font_rid, const uint8_t *p_data
fd->data_size = p_data_size;
}
+void TextServerFallback::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerFallback::font_get_style(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerFallback::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerFallback::font_get_style_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerFallback::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerFallback::font_get_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -841,7 +918,7 @@ void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased
}
bool TextServerFallback::font_is_antialiased(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -849,7 +926,7 @@ bool TextServerFallback::font_is_antialiased(RID p_font_rid) const {
}
void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -860,7 +937,7 @@ void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_
}
bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -868,7 +945,7 @@ bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_r
}
void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -879,7 +956,7 @@ void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pi
}
int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -887,7 +964,7 @@ int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const {
}
void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -898,7 +975,7 @@ void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
}
int TextServerFallback::font_get_msdf_size(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -906,7 +983,7 @@ int TextServerFallback::font_get_msdf_size(RID p_font_rid) const {
}
void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -916,7 +993,7 @@ void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
}
int TextServerFallback::font_get_fixed_size(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -924,7 +1001,7 @@ int TextServerFallback::font_get_fixed_size(RID p_font_rid) const {
}
void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -935,7 +1012,7 @@ void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_
}
bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -943,7 +1020,7 @@ bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const {
}
void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -954,7 +1031,7 @@ void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_
}
TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, HINTING_NONE);
MutexLock lock(fd->mutex);
@@ -962,7 +1039,7 @@ TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const {
}
void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -973,15 +1050,15 @@ void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Di
}
Dictionary TextServerFallback::font_get_variation_coordinates(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
return fd->variation_coordinates;
}
-void TextServerFallback::font_set_oversampling(RID p_font_rid, real_t p_oversampling) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_oversampling(RID p_font_rid, float p_oversampling) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -991,8 +1068,8 @@ void TextServerFallback::font_set_oversampling(RID p_font_rid, real_t p_oversamp
}
}
-real_t TextServerFallback::font_get_oversampling(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_oversampling(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1000,30 +1077,30 @@ real_t TextServerFallback::font_get_oversampling(RID p_font_rid) const {
}
Array TextServerFallback::font_get_size_cache_list(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
Array ret;
- for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ ret.push_back(E.key);
}
return ret;
}
void TextServerFallback::font_clear_size_cache(RID p_font_rid) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
- for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = fd->cache.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ memdelete(E.value);
}
fd->cache.clear();
}
void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1033,8 +1110,8 @@ void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &
}
}
-void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, float p_ascent) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1044,8 +1121,8 @@ void TextServerFallback::font_set_ascent(RID p_font_rid, int p_size, real_t p_as
fd->cache[size]->ascent = p_ascent;
}
-real_t TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1054,14 +1131,14 @@ real_t TextServerFallback::font_get_ascent(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->ascent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->ascent * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->ascent;
}
}
-void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, real_t p_descent) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, float p_descent) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
Vector2i size = _get_size(fd, p_size);
@@ -1070,8 +1147,8 @@ void TextServerFallback::font_set_descent(RID p_font_rid, int p_size, real_t p_d
fd->cache[size]->descent = p_descent;
}
-real_t TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1080,14 +1157,14 @@ real_t TextServerFallback::font_get_descent(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->descent * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->descent * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->descent;
}
}
-void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size, float p_underline_position) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1097,8 +1174,8 @@ void TextServerFallback::font_set_underline_position(RID p_font_rid, int p_size,
fd->cache[size]->underline_position = p_underline_position;
}
-real_t TextServerFallback::font_get_underline_position(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_underline_position(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1107,14 +1184,14 @@ real_t TextServerFallback::font_get_underline_position(RID p_font_rid, int p_siz
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->underline_position * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->underline_position * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->underline_position;
}
}
-void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size, float p_underline_thickness) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1124,8 +1201,8 @@ void TextServerFallback::font_set_underline_thickness(RID p_font_rid, int p_size
fd->cache[size]->underline_thickness = p_underline_thickness;
}
-real_t TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1134,14 +1211,14 @@ real_t TextServerFallback::font_get_underline_thickness(RID p_font_rid, int p_si
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->underline_thickness * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->underline_thickness * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->underline_thickness;
}
}
-void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, real_t p_scale) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, float p_scale) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1151,8 +1228,8 @@ void TextServerFallback::font_set_scale(RID p_font_rid, int p_size, real_t p_sca
fd->cache[size]->scale = p_scale;
}
-real_t TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+float TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0.f);
MutexLock lock(fd->mutex);
@@ -1161,14 +1238,14 @@ real_t TextServerFallback::font_get_scale(RID p_font_rid, int p_size) const {
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
if (fd->msdf) {
- return fd->cache[size]->scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->scale * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->scale / fd->cache[size]->oversampling;
}
}
void TextServerFallback::font_set_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing, int p_value) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1189,7 +1266,7 @@ void TextServerFallback::font_set_spacing(RID p_font_rid, int p_size, TextServer
}
int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1200,14 +1277,14 @@ int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer:
switch (p_spacing) {
case TextServer::SPACING_GLYPH: {
if (fd->msdf) {
- return fd->cache[size]->spacing_glyph * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->spacing_glyph * (float)p_size / (float)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 * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return fd->cache[size]->spacing_space * (float)p_size / (float)fd->msdf_source_size;
} else {
return fd->cache[size]->spacing_space;
}
@@ -1220,7 +1297,7 @@ int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer:
}
int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, 0);
MutexLock lock(fd->mutex);
@@ -1232,7 +1309,7 @@ int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p
}
void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *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);
@@ -1242,7 +1319,7 @@ void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_s
}
void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1250,11 +1327,11 @@ void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_s
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
ERR_FAIL_INDEX(p_texture_index, fd->cache[size]->textures.size());
- fd->cache[size]->textures.remove(p_texture_index);
+ fd->cache[size]->textures.remove_at(p_texture_index);
}
void TextServerFallback::font_set_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const Ref<Image> &p_image) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
ERR_FAIL_COND(p_image.is_null());
@@ -1280,7 +1357,7 @@ void TextServerFallback::font_set_texture_image(RID p_font_rid, const Vector2i &
}
Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Ref<Image>());
MutexLock lock(fd->mutex);
@@ -1295,12 +1372,13 @@ Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vect
}
void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index, const PackedInt32Array &p_offset) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *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);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ ERR_FAIL_COND(p_texture_index < 0);
if (p_texture_index >= fd->cache[size]->textures.size()) {
fd->cache[size]->textures.resize(p_texture_index + 1);
}
@@ -1310,7 +1388,7 @@ void TextServerFallback::font_set_texture_offsets(RID p_font_rid, const Vector2i
}
PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, PackedInt32Array());
MutexLock lock(fd->mutex);
@@ -1323,7 +1401,7 @@ PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, co
}
Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1340,7 +1418,7 @@ Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_
}
void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1351,7 +1429,7 @@ void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_siz
}
void TextServerFallback::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1362,7 +1440,7 @@ void TextServerFallback::font_remove_glyph(RID p_font_rid, const Vector2i &p_siz
}
Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1376,14 +1454,14 @@ Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, i
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].advance * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].advance * (float)p_size / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].advance;
}
}
void TextServerFallback::font_set_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph, const Vector2 &p_advance) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1398,7 +1476,7 @@ void TextServerFallback::font_set_glyph_advance(RID p_font_rid, int p_size, int3
}
Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1412,14 +1490,14 @@ Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.position * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].rect.position * (float)p_size.x / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].rect.position;
}
}
void TextServerFallback::font_set_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_offset) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1434,7 +1512,7 @@ void TextServerFallback::font_set_glyph_offset(RID p_font_rid, const Vector2i &p
}
Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1448,14 +1526,14 @@ Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &
const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
if (fd->msdf) {
- return gl[p_glyph].rect.size * (real_t)p_size.x / (real_t)fd->msdf_source_size;
+ return gl[p_glyph].rect.size * (float)p_size.x / (float)fd->msdf_source_size;
} else {
return gl[p_glyph].rect.size;
}
}
void TextServerFallback::font_set_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Vector2 &p_gl_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1470,7 +1548,7 @@ void TextServerFallback::font_set_glyph_size(RID p_font_rid, const Vector2i &p_s
}
Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Rect2());
MutexLock lock(fd->mutex);
@@ -1486,7 +1564,7 @@ Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i
}
void TextServerFallback::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, const Rect2 &p_uv_rect) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1501,7 +1579,7 @@ void TextServerFallback::font_set_glyph_uv_rect(RID p_font_rid, const Vector2i &
}
int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, -1);
MutexLock lock(fd->mutex);
@@ -1517,7 +1595,7 @@ int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2
}
void TextServerFallback::font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1531,42 +1609,50 @@ void TextServerFallback::font_set_glyph_texture_idx(RID p_font_rid, const Vector
gl[p_glyph].found = true;
}
-bool TextServerFallback::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
- ERR_FAIL_COND_V(!fd, false);
+Dictionary TextServerFallback::font_get_glyph_contours(RID p_font_rid, int p_size, int32_t p_index) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, p_size);
- ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ Vector<Vector3> points;
+ Vector<int32_t> contours;
+ bool orientation;
#ifdef MODULE_FREETYPE_ENABLED
- int error = FT_Load_Glyph(fd->cache[size]->face, p_index, FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
- ERR_FAIL_COND_V(error, false);
+ int error = FT_Load_Glyph(fd->cache[size]->face, FT_Get_Char_Index(fd->cache[size]->face, p_index), FT_LOAD_NO_BITMAP | (fd->force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0));
+ ERR_FAIL_COND_V(error, Dictionary());
- r_points.clear();
- r_contours.clear();
+ points.clear();
+ contours.clear();
- real_t h = fd->cache[size]->ascent;
- real_t scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
+ float h = fd->cache[size]->ascent;
+ float scale = (1.0 / 64.0) / fd->cache[size]->oversampling * fd->cache[size]->scale;
if (fd->msdf) {
- scale = scale * (real_t)p_size / (real_t)fd->msdf_source_size;
+ scale = scale * (float)p_size / (float)fd->msdf_source_size;
}
for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
- r_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, h - 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++) {
- r_contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
}
- r_orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+ orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
#else
- return false;
+ return Dictionary();
#endif
- return true;
+
+ Dictionary out;
+ out["points"] = points;
+ out["contours"] = contours;
+ out["orientation"] = orientation;
+ return out;
}
Array TextServerFallback::font_get_kerning_list(RID p_font_rid, int p_size) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Array());
MutexLock lock(fd->mutex);
@@ -1575,14 +1661,14 @@ Array TextServerFallback::font_get_kerning_list(RID p_font_rid, int p_size) cons
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
Array ret;
- for (const Map<Vector2i, Vector2>::Element *E = fd->cache[size]->kerning_map.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<Vector2i, Vector2> &E : fd->cache[size]->kerning_map) {
+ ret.push_back(E.key);
}
return ret;
}
void TextServerFallback::font_clear_kerning_map(RID p_font_rid, int p_size) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1593,7 +1679,7 @@ void TextServerFallback::font_clear_kerning_map(RID p_font_rid, int p_size) {
}
void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1604,7 +1690,7 @@ void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const V
}
void TextServerFallback::font_set_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair, const Vector2 &p_kerning) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1615,7 +1701,7 @@ void TextServerFallback::font_set_kerning(RID p_font_rid, int p_size, const Vect
}
Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector2());
MutexLock lock(fd->mutex);
@@ -1627,7 +1713,7 @@ Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const V
if (kern.has(p_glyph_pair)) {
if (fd->msdf) {
- return kern[p_glyph_pair] * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return kern[p_glyph_pair] * (float)p_size / (float)fd->msdf_source_size;
} else {
return kern[p_glyph_pair];
}
@@ -1635,9 +1721,11 @@ Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const V
#ifdef MODULE_FREETYPE_ENABLED
if (fd->cache[size]->face) {
FT_Vector delta;
- FT_Get_Kerning(fd->cache[size]->face, p_glyph_pair.x, p_glyph_pair.y, FT_KERNING_DEFAULT, &delta);
+ int32_t glyph_a = FT_Get_Char_Index(fd->cache[size]->face, p_glyph_pair.x);
+ int32_t glyph_b = FT_Get_Char_Index(fd->cache[size]->face, p_glyph_pair.y);
+ FT_Get_Kerning(fd->cache[size]->face, glyph_a, glyph_b, FT_KERNING_DEFAULT, &delta);
if (fd->msdf) {
- return Vector2(delta.x, delta.y) * (real_t)p_size / (real_t)fd->msdf_source_size;
+ return Vector2(delta.x, delta.y) * (float)p_size / (float)fd->msdf_source_size;
} else {
return Vector2(delta.x, delta.y);
}
@@ -1648,31 +1736,14 @@ Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const V
}
int32_t TextServerFallback::font_get_glyph_index(RID p_font_rid, int p_size, char32_t p_char, char32_t p_variation_selector) const {
- FontDataFallback *fd = font_owner.getornull(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);
-
-#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face) {
- if (p_variation_selector) {
- return FT_Face_GetCharVariantIndex(fd->cache[size]->face, p_char, p_variation_selector);
- } else {
- return FT_Get_Char_Index(fd->cache[size]->face, p_char);
- }
- } else {
- return 0;
- }
-#else
+ 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) + ".");
return (int32_t)p_char;
-#endif
}
bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *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) + ".");
MutexLock lock(fd->mutex);
if (fd->cache.is_empty()) {
@@ -1689,7 +1760,7 @@ bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const {
}
String TextServerFallback::font_get_supported_chars(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, String());
MutexLock lock(fd->mutex);
@@ -1723,25 +1794,21 @@ String TextServerFallback::font_get_supported_chars(RID p_font_rid) const {
}
void TextServerFallback::font_render_range(RID p_font_rid, const Vector2i &p_size, char32_t p_start, char32_t p_end) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *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) + ".");
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, p_size);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
for (char32_t i = p_start; i <= p_end; i++) {
-#ifdef MODULE_FREETYPE_ENABLED
- if (fd->cache[size]->face) {
- _ensure_glyph(fd, size, FT_Get_Char_Index(fd->cache[size]->face, i));
- continue;
- }
-#endif
_ensure_glyph(fd, size, (int32_t)i);
}
}
void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1751,14 +1818,14 @@ void TextServerFallback::font_render_glyph(RID p_font_rid, const Vector2i &p_siz
}
void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ 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));
if (!_ensure_glyph(fd, size, p_index)) {
- return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
}
const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
@@ -1776,13 +1843,13 @@ void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
if (fd->msdf) {
Point2 cpos = p_pos;
- cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
- Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size;
+ cpos += gl.rect.position * (float)p_size / (float)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, 0, fd->msdf_range);
} else {
- Point2i cpos = p_pos;
+ Point2 cpos = p_pos.floor();
cpos += gl.rect.position;
- Size2i csize = gl.rect.size;
+ Size2 csize = gl.rect.size;
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
}
}
@@ -1791,14 +1858,14 @@ void TextServerFallback::font_draw_glyph(RID p_font_rid, RID p_canvas, int p_siz
}
void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, int p_size, int p_outline_size, const Vector2 &p_pos, int32_t p_index, const Color &p_color) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size_outline(fd, Vector2i(p_size, p_outline_size));
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
if (!_ensure_glyph(fd, size, p_index)) {
- return; // // Invalid or non graphicl glyph, do not display errors, nothing to draw.
+ return; // Invalid or non-graphical glyph, do not display errors, nothing to draw.
}
const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
@@ -1816,13 +1883,13 @@ void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
if (fd->msdf) {
Point2 cpos = p_pos;
- cpos += gl.rect.position * (real_t)p_size / (real_t)fd->msdf_source_size;
- Size2 csize = gl.rect.size * (real_t)p_size / (real_t)fd->msdf_source_size;
+ cpos += gl.rect.position * (float)p_size / (float)fd->msdf_source_size;
+ Size2 csize = gl.rect.size * (float)p_size / (float)fd->msdf_source_size;
RenderingServer::get_singleton()->canvas_item_add_msdf_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, p_outline_size * 2, fd->msdf_range);
} else {
- Point2i cpos = p_pos;
+ Point2 cpos = p_pos.floor();
cpos += gl.rect.position;
- Size2i csize = gl.rect.size;
+ Size2 csize = gl.rect.size;
RenderingServer::get_singleton()->canvas_item_add_texture_rect_region(p_canvas, Rect2(cpos, csize), texture, gl.uv_rect, modulate, false, false);
}
}
@@ -1831,7 +1898,7 @@ void TextServerFallback::font_draw_glyph_outline(RID p_font_rid, RID p_canvas, i
}
bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String &p_language) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1843,7 +1910,7 @@ bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String
}
void TextServerFallback::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1851,7 +1918,7 @@ void TextServerFallback::font_set_language_support_override(RID p_font_rid, cons
}
bool TextServerFallback::font_get_language_support_override(RID p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1859,7 +1926,7 @@ bool TextServerFallback::font_get_language_support_override(RID p_font_rid, cons
}
void TextServerFallback::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1867,19 +1934,19 @@ void TextServerFallback::font_remove_language_support_override(RID p_font_rid, c
}
Vector<String> TextServerFallback::font_get_language_support_overrides(RID p_font_rid) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
MutexLock lock(fd->mutex);
Vector<String> out;
- for (const Map<String, bool>::Element *E = fd->language_support_overrides.front(); E; E = E->next()) {
- out.push_back(E->key());
+ for (const KeyValue<String, bool> &E : fd->language_support_overrides) {
+ out.push_back(E.key);
}
return out;
}
bool TextServerFallback::font_is_script_supported(RID p_font_rid, const String &p_script) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1891,7 +1958,7 @@ bool TextServerFallback::font_is_script_supported(RID p_font_rid, const String &
}
void TextServerFallback::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1899,7 +1966,7 @@ void TextServerFallback::font_set_script_support_override(RID p_font_rid, const
}
bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, false);
MutexLock lock(fd->mutex);
@@ -1907,7 +1974,7 @@ bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const
}
void TextServerFallback::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
@@ -1917,13 +1984,13 @@ void TextServerFallback::font_remove_script_support_override(RID p_font_rid, con
}
Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font_rid) {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Vector<String>());
MutexLock lock(fd->mutex);
Vector<String> out;
- for (const Map<String, bool>::Element *E = fd->script_support_overrides.front(); E; E = E->next()) {
- out.push_back(E->key());
+ for (const KeyValue<String, bool> &E : fd->script_support_overrides) {
+ out.push_back(E.key);
}
return out;
}
@@ -1933,7 +2000,7 @@ Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const
}
Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) const {
- FontDataFallback *fd = font_owner.getornull(p_font_rid);
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
@@ -1942,11 +2009,11 @@ Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) con
return fd->supported_varaitions;
}
-real_t TextServerFallback::font_get_global_oversampling() const {
+float TextServerFallback::font_get_global_oversampling() const {
return oversampling;
}
-void TextServerFallback::font_set_global_oversampling(real_t p_oversampling) {
+void TextServerFallback::font_set_global_oversampling(float p_oversampling) {
_THREAD_SAFE_METHOD_
if (oversampling != p_oversampling) {
oversampling = p_oversampling;
@@ -1964,7 +2031,7 @@ void TextServerFallback::font_set_global_oversampling(real_t p_oversampling) {
List<RID> text_bufs;
shaped_owner.get_owned_list(&text_bufs);
for (const RID &E : text_bufs) {
- invalidate(shaped_owner.getornull(E));
+ invalidate(shaped_owner.get_or_null(E));
}
}
}
@@ -1989,11 +2056,11 @@ void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
}
void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
- ShapedTextData *parent = shaped_owner.getornull(p_shaped->parent);
+ ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent);
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = parent->objects.front(); E; E = E->next()) {
- if (E->get().pos >= p_shaped->start && E->get().pos < p_shaped->end) {
- p_shaped->objects[E->key()] = E->get();
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : parent->objects) {
+ if (E.value.pos >= p_shaped->start && E.value.pos < p_shaped->end) {
+ p_shaped->objects[E.key] = E.value;
}
}
@@ -2020,7 +2087,7 @@ RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, Te
}
void TextServerFallback::shaped_text_clear(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2043,8 +2110,29 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped
return TextServer::DIRECTION_LTR;
}
+void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->custom_punct != p_punct) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->custom_punct = p_punct;
+ invalidate(sd);
+ }
+}
+
+String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, String());
+ return sd->custom_punct;
+}
+
void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2057,12 +2145,12 @@ void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::O
}
}
-void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) {
+void TextServerFallback::shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) {
// No BiDi support, ignore.
}
TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
MutexLock lock(sd->mutex);
@@ -2070,7 +2158,7 @@ TextServer::Orientation TextServerFallback::shaped_text_get_orientation(RID p_sh
}
void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
MutexLock lock(sd->mutex);
ERR_FAIL_COND(!sd);
@@ -2084,7 +2172,7 @@ void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2092,7 +2180,7 @@ bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
}
void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND(!sd);
MutexLock lock(sd->mutex);
@@ -2106,7 +2194,7 @@ void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_e
}
bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2114,14 +2202,14 @@ bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
}
bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features, const String &p_language) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
ERR_FAIL_COND_V(p_size <= 0, false);
for (int i = 0; i < p_fonts.size(); i++) {
- ERR_FAIL_COND_V(!font_owner.getornull(p_fonts[i]), false);
+ ERR_FAIL_COND_V(!font_owner.get_or_null(p_fonts[i]), false);
}
if (p_text.is_empty()) {
@@ -2160,8 +2248,8 @@ bool TextServerFallback::shaped_text_add_string(RID p_shaped, const String &p_te
return true;
}
-bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align, int p_length) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align, int p_length) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2173,7 +2261,7 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
}
ShapedTextData::Span span;
- span.start = sd->text.length();
+ span.start = sd->start + sd->text.length();
span.end = span.start + p_length;
span.embedded_key = p_key;
@@ -2191,8 +2279,8 @@ bool TextServerFallback::shaped_text_add_object(RID p_shaped, Variant p_key, con
return true;
}
-bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2212,9 +2300,9 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
Glyph gl = sd->glyphs[i];
Variant key;
if (gl.count == 1) {
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if (E->get().pos == gl.start) {
- key = E->key();
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if (E.value.pos == gl.start) {
+ key = E.key;
break;
}
}
@@ -2243,8 +2331,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
} else if (sd->preserve_invalid || (sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
- sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y);
} else {
sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
@@ -2255,66 +2342,66 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
// Align embedded objects to baseline.
- real_t full_ascent = sd->ascent;
- real_t full_descent = sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if ((E->get().pos >= sd->start) && (E->get().pos < sd->end)) {
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if ((E.value.pos >= sd->start) && (E.value.pos < sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
@@ -2325,7 +2412,7 @@ bool TextServerFallback::shaped_text_resize_object(RID p_shaped, Variant p_key,
}
RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
@@ -2346,6 +2433,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->orientation = sd->orientation;
new_sd->direction = sd->direction;
+ new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
new_sd->line_breaks_valid = sd->line_breaks_valid;
new_sd->justification_ops_valid = sd->justification_ops_valid;
@@ -2354,7 +2442,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
new_sd->uthk = sd->uthk;
if (p_length > 0) {
- new_sd->text = sd->text.substr(p_start, p_length);
+ new_sd->text = sd->text.substr(p_start - sd->start, p_length);
int sd_size = sd->glyphs.size();
const Glyph *sd_glyphs = sd->glyphs.ptr();
@@ -2364,11 +2452,11 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
Variant key;
bool find_embedded = false;
if (gl.count == 1) {
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- if (E->get().pos == gl.start) {
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if (E.value.pos == gl.start) {
find_embedded = true;
- key = E->key();
- new_sd->objects[key] = E->get();
+ key = E.key;
+ new_sd->objects[key] = E.value;
break;
}
}
@@ -2393,8 +2481,7 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
} else if (new_sd->preserve_invalid || (new_sd->preserve_control && is_control(gl.index))) {
// Glyph not found, replace with hex code box.
if (new_sd->orientation == ORIENTATION_HORIZONTAL) {
- new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
- new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ new_sd->ascent = MAX(new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y);
} else {
new_sd->ascent = MAX(new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
new_sd->descent = MAX(new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
@@ -2407,66 +2494,66 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
// Align embedded objects to baseline.
- real_t full_ascent = new_sd->ascent;
- real_t full_descent = new_sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = new_sd->objects.front(); E; E = E->next()) {
- if ((E->get().pos >= new_sd->start) && (E->get().pos < new_sd->end)) {
+ float full_ascent = new_sd->ascent;
+ float full_descent = new_sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : new_sd->objects) {
+ if ((E.value.pos >= new_sd->start) && (E.value.pos < new_sd->end)) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -new_sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -new_sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = new_sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = new_sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -new_sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -new_sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = new_sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = new_sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
}
@@ -2479,15 +2566,15 @@ RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_leng
}
RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, RID());
MutexLock lock(sd->mutex);
return sd->parent;
}
-real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2550,12 +2637,12 @@ real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width
}
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
- real_t delta_width_per_space = (p_width - sd->width) / space_count;
+ float delta_width_per_space = (p_width - sd->width) / space_count;
for (int i = start_pos; i <= end_pos; i++) {
Glyph &gl = sd->glyphs.write[i];
if (gl.count > 0) {
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
- real_t old_adv = gl.advance;
+ float old_adv = gl.advance;
gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
sd->width += (gl.advance - old_adv);
}
@@ -2566,8 +2653,8 @@ real_t TextServerFallback::shaped_text_fit_to_width(RID p_shaped, real_t p_width
return sd->width;
}
-real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -2579,7 +2666,7 @@ real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real
}
int tab_index = 0;
- real_t off = 0.f;
+ float off = 0.f;
int start, end, delta;
if (sd->para_direction == DIRECTION_LTR) {
@@ -2596,7 +2683,7 @@ real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real
for (int i = start; i != end; i += delta) {
if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
- real_t tab_off = 0.f;
+ float tab_off = 0.f;
while (tab_off <= off) {
tab_off += p_tab_stops[tab_index];
tab_index++;
@@ -2604,7 +2691,7 @@ real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real
tab_index = 0;
}
}
- real_t old_adv = gl[i].advance;
+ float old_adv = gl[i].advance;
gl[i].advance = tab_off - off;
sd->width += gl[i].advance - old_adv;
off = 0;
@@ -2617,7 +2704,7 @@ real_t TextServerFallback::shaped_text_tab_align(RID p_shaped, const Vector<real
}
bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2630,27 +2717,41 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
int sd_size = sd->glyphs.size();
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ int c_punct_size = sd->custom_punct.length();
+ const char32_t *c_punct = sd->custom_punct.ptr();
+
for (int i = 0; i < sd_size; i++) {
- if (sd->glyphs[i].count > 0) {
- char32_t c = sd->text[sd->glyphs[i].start];
- if (is_punct(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ if (sd_glyphs[i].count > 0) {
+ char32_t c = sd->text[sd_glyphs[i].start - sd->start];
+ if (c_punct_size == 0) {
+ if (is_punct(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ }
+ } else {
+ for (int j = 0; j < c_punct_size; j++) {
+ if (c_punct[j] == c) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_PUNCTUATION;
+ break;
+ }
+ }
}
if (is_underscore(c)) {
sd->glyphs.write[i].flags |= GRAPHEME_IS_UNDERSCORE;
}
if (is_whitespace(c) && !is_linebreak(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_SPACE;
- sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
}
if (is_linebreak(c)) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
}
if (c == 0x0009 || c == 0x000b) {
- sd->glyphs.write[i].flags |= GRAPHEME_IS_TAB;
+ sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
}
- i += (sd->glyphs[i].count - 1);
+ i += (sd_glyphs[i].count - 1);
}
}
sd->line_breaks_valid = true;
@@ -2658,7 +2759,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
}
bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2673,8 +2774,8 @@ bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
return true;
}
-void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, real_t p_width, uint8_t p_trim_flags) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped_line);
+void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, float p_width, uint16_t p_trim_flags) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped_line);
ERR_FAIL_COND_MSG(!sd, "ShapedTextDataFallback invalid.");
MutexLock lock(sd->mutex);
@@ -2682,6 +2783,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
shaped_text_shape(p_shaped_line);
}
+ sd->text_trimmed = false;
sd->overrun_trim_data.ellipsis_glyph_buf.clear();
bool add_ellipsis = (p_trim_flags & OVERRUN_ADD_ELLIPSIS) == OVERRUN_ADD_ELLIPSIS;
@@ -2715,7 +2817,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
}
int ell_min_characters = 6;
- real_t width = sd->width;
+ float width = sd->width;
int trim_pos = 0;
int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
@@ -2727,10 +2829,10 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
if (sd_glyphs[i].count > 0) {
- bool above_min_char_treshold = (i >= ell_min_characters);
+ bool above_min_char_threshold = (i >= ell_min_characters);
- if (width + (((above_min_char_treshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
- if (cut_per_word && above_min_char_treshold) {
+ if (width + (((above_min_char_threshold && add_ellipsis) || enforce_ellipsis) ? ellipsis_width : 0) <= p_width) {
+ if (cut_per_word && above_min_char_threshold) {
if ((sd_glyphs[i].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT) {
last_valid_cut = i;
found = true;
@@ -2742,7 +2844,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
if (found) {
trim_pos = last_valid_cut;
- if (add_ellipsis && (above_min_char_treshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
ellipsis_pos = trim_pos;
}
break;
@@ -2761,7 +2863,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
if (add_ellipsis && (ellipsis_pos > 0 || enforce_ellipsis)) {
// Insert an additional space when cutting word bound for aesthetics.
if (cut_per_word && (ellipsis_pos > 0)) {
- TextServer::Glyph gl;
+ Glyph gl;
gl.count = 1;
gl.advance = whitespace_adv.x;
gl.index = whitespace_gl_idx;
@@ -2772,7 +2874,7 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
}
// Add ellipsis dots.
- TextServer::Glyph gl;
+ Glyph gl;
gl.count = 1;
gl.repeat = 3;
gl.advance = dot_adv.x;
@@ -2789,16 +2891,40 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, re
}
}
-TextServer::TrimData TextServerFallback::shaped_text_get_trim_data(RID p_shaped) const {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V_MSG(!sd, TrimData(), "ShapedTextDataFallback invalid.");
+int TextServerFallback::shaped_text_get_trim_pos(RID p_shaped) const {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.trim_pos;
+}
+
+int TextServerFallback::shaped_text_get_ellipsis_pos(RID p_shaped) const {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, -1, "ShapedTextData invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.ellipsis_pos;
+}
+
+const Glyph *TextServerFallback::shaped_text_get_ellipsis_glyphs(RID p_shaped) const {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, nullptr, "ShapedTextData invalid.");
MutexLock lock(sd->mutex);
- return sd->overrun_trim_data;
+ return sd->overrun_trim_data.ellipsis_glyph_buf.ptr();
+}
+
+int TextServerFallback::shaped_text_get_ellipsis_glyph_count(RID p_shaped) const {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V_MSG(!sd, 0, "ShapedTextData invalid.");
+
+ MutexLock lock(sd->mutex);
+ return sd->overrun_trim_data.ellipsis_glyph_buf.size();
}
bool TextServerFallback::shaped_text_shape(RID p_shaped) {
- ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
@@ -2855,7 +2981,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
gl.end = j + 1;
gl.count = 1;
gl.font_size = span.font_size;
- gl.index = (int32_t)sd->text[j]; // Use codepoint.
+ gl.index = (int32_t)sd->text[j - sd->start]; // Use codepoint.
if (gl.index == 0x0009 || gl.index == 0x000b) {
gl.index = 0x0020;
}
@@ -2871,7 +2997,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
if (gl.font_rid.is_valid()) {
- if (sd->text[j] != 0 && !is_linebreak(sd->text[j])) {
+ if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
gl.x_off = 0;
@@ -2886,7 +3012,7 @@ bool TextServerFallback::shaped_text_shape(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])) {
+ 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);
@@ -2909,8 +3035,7 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
// Glyph not found, replace with hex code box.
if (sd->orientation == ORIENTATION_HORIZONTAL) {
gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;
- sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.75f));
- sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).y * 0.25f));
+ sd->ascent = MAX(sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y);
} else {
gl.advance = get_hex_code_box_size(gl.font_size, gl.index).y;
sd->ascent = MAX(sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
@@ -2924,65 +3049,65 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
// Align embedded objects to baseline.
- real_t full_ascent = sd->ascent;
- real_t full_descent = sd->descent;
- for (Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
+ float full_ascent = sd->ascent;
+ float full_descent = sd->descent;
+ for (KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
if (sd->orientation == ORIENTATION_HORIZONTAL) {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.y = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.y = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.y = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.y = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.y = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.y -= E->get().rect.size.y;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.y -= E->get().rect.size.y / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.y);
- full_descent = MAX(full_descent, E->get().rect.position.y + E->get().rect.size.y);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.y);
+ full_descent = MAX(full_descent, E.value.rect.position.y + E.value.rect.size.y);
} else {
- switch (E->get().inline_align & INLINE_ALIGN_TEXT_MASK) {
- case INLINE_ALIGN_TO_TOP: {
- E->get().rect.position.x = -sd->ascent;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_TEXT_MASK) {
+ case INLINE_ALIGNMENT_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
} break;
- case INLINE_ALIGN_TO_CENTER: {
- E->get().rect.position.x = (-sd->ascent + sd->descent) / 2;
+ case INLINE_ALIGNMENT_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
} break;
- case INLINE_ALIGN_TO_BASELINE: {
- E->get().rect.position.x = 0;
+ case INLINE_ALIGNMENT_TO_BASELINE: {
+ E.value.rect.position.x = 0;
} break;
- case INLINE_ALIGN_TO_BOTTOM: {
- E->get().rect.position.x = sd->descent;
+ case INLINE_ALIGNMENT_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
} break;
}
- switch (E->get().inline_align & INLINE_ALIGN_IMAGE_MASK) {
- case INLINE_ALIGN_BOTTOM_TO: {
- E->get().rect.position.x -= E->get().rect.size.x;
+ switch (E.value.inline_align & INLINE_ALIGNMENT_IMAGE_MASK) {
+ case INLINE_ALIGNMENT_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
} break;
- case INLINE_ALIGN_CENTER_TO: {
- E->get().rect.position.x -= E->get().rect.size.x / 2;
+ case INLINE_ALIGNMENT_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
} break;
- case INLINE_ALIGN_TOP_TO: {
- //NOP
+ case INLINE_ALIGNMENT_TOP_TO: {
+ // NOP
} break;
}
- full_ascent = MAX(full_ascent, -E->get().rect.position.x);
- full_descent = MAX(full_descent, E->get().rect.position.x + E->get().rect.size.x);
+ full_ascent = MAX(full_ascent, -E.value.rect.position.x);
+ full_descent = MAX(full_descent, E.value.rect.position.x + E.value.rect.size.x);
}
}
sd->ascent = full_ascent;
@@ -2992,59 +3117,70 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
}
bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, false);
MutexLock lock(sd->mutex);
return sd->valid;
}
-Vector<TextServer::Glyph> TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+const Glyph *TextServerFallback::shaped_text_get_glyphs(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->glyphs;
+ return sd->glyphs.ptr();
}
-Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector2i());
+int TextServerFallback::shaped_text_get_glyph_count(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, 0);
MutexLock lock(sd->mutex);
- return Vector2(sd->start, sd->end);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs.size();
}
-Vector<TextServer::Glyph> TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
- ERR_FAIL_COND_V(!sd, Vector<TextServer::Glyph>());
+const Glyph *TextServerFallback::shaped_text_sort_logical(RID p_shaped) {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, nullptr);
MutexLock lock(sd->mutex);
if (!sd->valid) {
const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
}
- return sd->glyphs; // Already in the logical order, return as is.
+ return sd->glyphs.ptr(); // Already in the logical order, return as is.
+}
+
+Vector2i TextServerFallback::shaped_text_get_range(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Vector2i());
+
+ MutexLock lock(sd->mutex);
+ return Vector2(sd->start, sd->end);
}
Array TextServerFallback::shaped_text_get_objects(RID p_shaped) const {
Array ret;
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, ret);
MutexLock lock(sd->mutex);
- for (const Map<Variant, ShapedTextData::EmbeddedObject>::Element *E = sd->objects.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ ret.push_back(E.key);
}
return ret;
}
Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_key) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Rect2());
MutexLock lock(sd->mutex);
@@ -3056,7 +3192,7 @@ Rect2 TextServerFallback::shaped_text_get_object_rect(RID p_shaped, Variant p_ke
}
Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, Size2());
MutexLock lock(sd->mutex);
@@ -3070,8 +3206,8 @@ Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
}
}
-real_t TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3081,8 +3217,8 @@ real_t TextServerFallback::shaped_text_get_ascent(RID p_shaped) const {
return sd->ascent;
}
-real_t TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3092,8 +3228,8 @@ real_t TextServerFallback::shaped_text_get_descent(RID p_shaped) const {
return sd->descent;
}
-real_t TextServerFallback::shaped_text_get_width(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_get_width(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3103,8 +3239,8 @@ real_t TextServerFallback::shaped_text_get_width(RID p_shaped) const {
return sd->width;
}
-real_t TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_get_underline_position(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3115,8 +3251,8 @@ real_t TextServerFallback::shaped_text_get_underline_position(RID p_shaped) cons
return sd->upos;
}
-real_t TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
- const ShapedTextData *sd = shaped_owner.getornull(p_shaped);
+float TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
ERR_FAIL_COND_V(!sd, 0.f);
MutexLock lock(sd->mutex);
@@ -3127,19 +3263,14 @@ real_t TextServerFallback::shaped_text_get_underline_thickness(RID p_shaped) con
return sd->uthk;
}
-TextServer *TextServerFallback::create_func(Error &r_error, void *p_user_data) {
- r_error = OK;
- return memnew(TextServerFallback());
-}
-
-void TextServerFallback::register_server() {
- TextServerManager::register_create_function(interface_name, interface_features, create_func, nullptr);
-}
-
-TextServerFallback::TextServerFallback(){};
+TextServerFallback::TextServerFallback() {
+ _insert_feature_sets();
+};
TextServerFallback::~TextServerFallback() {
+#ifdef MODULE_FREETYPE_ENABLED
if (library != nullptr) {
FT_Done_FreeType(library);
}
+#endif
};
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index fde75e7135..f2b33c2afb 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -42,7 +42,7 @@
#include "core/templates/thread_work_pool.h"
#include "scene/resources/texture.h"
-#include "modules/modules_enabled.gen.h"
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
#ifdef MODULE_FREETYPE_ENABLED
#include <ft2build.h>
@@ -54,6 +54,8 @@
#include FT_BBOX_H
#endif
+#define OT_TAG(c1, c2, c3, c4) ((int32_t)((((uint32_t)(c1)&0xFF) << 24) | (((uint32_t)(c2)&0xFF) << 16) | (((uint32_t)(c3)&0xFF) << 8) | ((uint32_t)(c4)&0xFF)))
+
class TextServerFallback : public TextServer {
GDCLASS(TextServerFallback, TextServer);
_THREAD_SAFE_CLASS_
@@ -61,6 +63,10 @@ class TextServerFallback : public TextServer {
static String interface_name;
static uint32_t interface_features;
+ Map<StringName, int32_t> feature_sets;
+
+ void _insert_feature_sets();
+
// Font cache data.
#ifdef MODULE_FREETYPE_ENABLED
@@ -93,12 +99,12 @@ class TextServerFallback : public TextServer {
};
struct FontDataForSizeFallback {
- real_t ascent = 0.f;
- real_t descent = 0.f;
- real_t underline_position = 0.f;
- real_t underline_thickness = 0.f;
- real_t scale = 1.f;
- real_t oversampling = 1.f;
+ float ascent = 0.f;
+ float descent = 0.f;
+ float underline_position = 0.f;
+ float underline_thickness = 0.f;
+ float scale = 1.f;
+ float oversampling = 1.f;
int spacing_glyph = 0;
int spacing_space = 0;
@@ -134,7 +140,11 @@ class TextServerFallback : public TextServer {
bool force_autohinter = false;
TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
Dictionary variation_coordinates;
- real_t oversampling = 0.f;
+ float oversampling = 0.f;
+
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
Map<Vector2i, FontDataForSizeFallback *> cache;
@@ -194,7 +204,7 @@ class TextServerFallback : public TextServer {
// Common data.
- real_t oversampling = 1.f;
+ float oversampling = 1.f;
mutable RID_PtrOwner<FontDataFallback> font_owner;
mutable RID_PtrOwner<ShapedTextData> shaped_owner;
@@ -205,20 +215,19 @@ protected:
void invalidate(ShapedTextData *p_shaped);
public:
- virtual bool has_feature(Feature p_feature) override;
+ virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
+ virtual uint32_t get_features() const override;
virtual void free(RID p_rid) override;
virtual bool has(RID p_rid) override;
virtual bool load_support_data(const String &p_filename) override;
-#ifdef TOOLS_ENABLED
- virtual String get_support_data_filename() override { return ""; };
- virtual String get_support_data_info() override { return "Not supported"; };
- virtual bool save_support_data(const String &p_filename) override;
-#endif
+ virtual String get_support_data_filename() const override { return ""; };
+ virtual String get_support_data_info() const override { return "Not supported"; };
+ virtual bool save_support_data(const String &p_filename) const override;
- virtual bool is_locale_right_to_left(const String &p_locale) override;
+ virtual bool is_locale_right_to_left(const String &p_locale) const override;
virtual int32_t name_to_tag(const String &p_name) const override;
virtual String tag_to_name(int32_t p_tag) const override;
@@ -229,6 +238,15 @@ public:
virtual void font_set_data(RID p_font_rid, const PackedByteArray &p_data) override;
virtual void font_set_data_ptr(RID p_font_rid, const uint8_t *p_data_ptr, size_t p_data_size) override;
+ virtual void font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) override;
+ virtual uint32_t /*FontStyle*/ font_get_style(RID p_font_rid) const override;
+
+ virtual void font_set_style_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_style_name(RID p_font_rid) const override;
+
+ virtual void font_set_name(RID p_font_rid, const String &p_name) override;
+ virtual String font_get_name(RID p_font_rid) const override;
+
virtual void font_set_antialiased(RID p_font_rid, bool p_antialiased) override;
virtual bool font_is_antialiased(RID p_font_rid) const override;
@@ -253,27 +271,27 @@ public:
virtual void font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) override;
virtual Dictionary font_get_variation_coordinates(RID p_font_rid) const override;
- virtual void font_set_oversampling(RID p_font_rid, real_t p_oversampling) override;
- virtual real_t font_get_oversampling(RID p_font_rid) const override;
+ virtual void font_set_oversampling(RID p_font_rid, float p_oversampling) override;
+ virtual float font_get_oversampling(RID p_font_rid) const override;
virtual Array font_get_size_cache_list(RID p_font_rid) const override;
virtual void font_clear_size_cache(RID p_font_rid) override;
virtual void font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) override;
- virtual void font_set_ascent(RID p_font_rid, int p_size, real_t p_ascent) override;
- virtual real_t font_get_ascent(RID p_font_rid, int p_size) const override;
+ virtual void font_set_ascent(RID p_font_rid, int p_size, float p_ascent) override;
+ virtual float font_get_ascent(RID p_font_rid, int p_size) const override;
- virtual void font_set_descent(RID p_font_rid, int p_size, real_t p_descent) override;
- virtual real_t font_get_descent(RID p_font_rid, int p_size) const override;
+ virtual void font_set_descent(RID p_font_rid, int p_size, float p_descent) override;
+ virtual float font_get_descent(RID p_font_rid, int p_size) const override;
- virtual void font_set_underline_position(RID p_font_rid, int p_size, real_t p_underline_position) override;
- virtual real_t font_get_underline_position(RID p_font_rid, int p_size) const override;
+ virtual void font_set_underline_position(RID p_font_rid, int p_size, float p_underline_position) override;
+ virtual float font_get_underline_position(RID p_font_rid, int p_size) const override;
- virtual void font_set_underline_thickness(RID p_font_rid, int p_size, real_t p_underline_thickness) override;
- virtual real_t font_get_underline_thickness(RID p_font_rid, int p_size) const override;
+ virtual void font_set_underline_thickness(RID p_font_rid, int p_size, float p_underline_thickness) override;
+ virtual float font_get_underline_thickness(RID p_font_rid, int p_size) const override;
- virtual void font_set_scale(RID p_font_rid, int p_size, real_t p_scale) override;
- virtual real_t font_get_scale(RID p_font_rid, int p_size) const override;
+ virtual void font_set_scale(RID p_font_rid, int p_size, float p_scale) override;
+ virtual float font_get_scale(RID p_font_rid, int p_size) const override;
virtual void font_set_spacing(RID p_font_rid, int p_size, SpacingType p_spacing, int p_value) override;
virtual int font_get_spacing(RID p_font_rid, int p_size, SpacingType p_spacing) const override;
@@ -307,7 +325,7 @@ public:
virtual int font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const override;
virtual void font_set_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph, int p_texture_idx) override;
- virtual bool font_get_glyph_contours(RID p_font, int p_size, int32_t p_index, Vector<Vector3> &r_points, Vector<int32_t> &r_contours, bool &r_orientation) const override;
+ virtual Dictionary font_get_glyph_contours(RID p_font, int p_size, int32_t p_index) const override;
virtual Array font_get_kerning_list(RID p_font_rid, int p_size) const override;
virtual void font_clear_kerning_map(RID p_font_rid, int p_size) override;
@@ -342,8 +360,8 @@ public:
virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
- virtual real_t font_get_global_oversampling() const override;
- virtual void font_set_global_oversampling(real_t p_oversampling) override;
+ virtual float font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(float p_oversampling) override;
/* Shaped text buffer interface */
@@ -354,7 +372,10 @@ public:
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
- virtual void shaped_text_set_bidi_override(RID p_shaped, const Vector<Vector2i> &p_override) override;
+ virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
+
+ virtual void shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) override;
+ virtual String shaped_text_get_custom_punctuation(RID p_shaped) const override;
virtual void shaped_text_set_orientation(RID p_shaped, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
virtual Orientation shaped_text_get_orientation(RID p_shaped) const override;
@@ -366,42 +387,43 @@ public:
virtual bool shaped_text_get_preserve_control(RID p_shaped) const override;
virtual bool shaped_text_add_string(RID p_shaped, const String &p_text, const Vector<RID> &p_fonts, int p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "") override;
- virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER, int p_length = 1) override;
- virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlign p_inline_align = INLINE_ALIGN_CENTER) override;
+ virtual bool shaped_text_add_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int p_length = 1) override;
+ virtual bool shaped_text_resize_object(RID p_shaped, Variant p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER) override;
virtual RID shaped_text_substr(RID p_shaped, int p_start, int p_length) const override;
virtual RID shaped_text_get_parent(RID p_shaped) const override;
- virtual real_t shaped_text_fit_to_width(RID p_shaped, real_t p_width, uint8_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
- virtual real_t shaped_text_tab_align(RID p_shaped, const Vector<real_t> &p_tab_stops) override;
+ virtual float shaped_text_fit_to_width(RID p_shaped, float p_width, uint16_t /*JustificationFlag*/ p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
+ virtual float shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) override;
virtual bool shaped_text_shape(RID p_shaped) override;
virtual bool shaped_text_update_breaks(RID p_shaped) override;
virtual bool shaped_text_update_justification_ops(RID p_shaped) override;
- virtual void shaped_text_overrun_trim_to_width(RID p_shaped, real_t p_width, uint8_t p_trim_flags) override;
- virtual TrimData shaped_text_get_trim_data(RID p_shaped) const override;
+ virtual int shaped_text_get_trim_pos(RID p_shaped) const override;
+ virtual int shaped_text_get_ellipsis_pos(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_get_ellipsis_glyphs(RID p_shaped) const override;
+ virtual int shaped_text_get_ellipsis_glyph_count(RID p_shaped) const override;
+
+ virtual void shaped_text_overrun_trim_to_width(RID p_shaped, float p_width, uint16_t p_trim_flags) override;
virtual bool shaped_text_is_ready(RID p_shaped) const override;
- virtual Vector<Glyph> shaped_text_get_glyphs(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_get_glyphs(RID p_shaped) const override;
+ virtual const Glyph *shaped_text_sort_logical(RID p_shaped) override;
+ virtual int shaped_text_get_glyph_count(RID p_shaped) const override;
virtual Vector2i shaped_text_get_range(RID p_shaped) const override;
- virtual Vector<Glyph> shaped_text_sort_logical(RID p_shaped) override;
-
virtual Array shaped_text_get_objects(RID p_shaped) const override;
virtual Rect2 shaped_text_get_object_rect(RID p_shaped, Variant p_key) const override;
virtual Size2 shaped_text_get_size(RID p_shaped) const override;
- virtual real_t shaped_text_get_ascent(RID p_shaped) const override;
- virtual real_t shaped_text_get_descent(RID p_shaped) const override;
- virtual real_t shaped_text_get_width(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_position(RID p_shaped) const override;
- virtual real_t shaped_text_get_underline_thickness(RID p_shaped) const override;
-
- static TextServer *create_func(Error &r_error, void *p_user_data);
- static void register_server();
+ virtual float shaped_text_get_ascent(RID p_shaped) const override;
+ virtual float shaped_text_get_descent(RID p_shaped) const override;
+ virtual float shaped_text_get_width(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_position(RID p_shaped) const override;
+ virtual float shaped_text_get_underline_thickness(RID p_shaped) const override;
TextServerFallback();
~TextServerFallback();
diff --git a/modules/theora/config.py b/modules/theora/config.py
index b063ed51f9..7f354a8fda 100644
--- a/modules/theora/config.py
+++ b/modules/theora/config.py
@@ -1,4 +1,6 @@
def can_build(env, platform):
+ if env["arch"].startswith("rv"):
+ return False
return env.module_check_dependencies("theora", ["ogg", "vorbis"])
diff --git a/modules/theora/doc_classes/VideoStreamTheora.xml b/modules/theora/doc_classes/VideoStreamTheora.xml
index e7bf9b202d..725f87b046 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -4,7 +4,7 @@
[VideoStream] resource for Ogg Theora videos.
</brief_description>
<description>
- [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is less efficient than [VideoStreamWebm]'s VP8 and VP9, but it requires less CPU resources to decode. The Theora codec is decoded on the CPU.
+ [VideoStream] resource handling the [url=https://www.theora.org/]Ogg Theora[/url] video format with [code].ogv[/code] extension. The Theora codec is decoded on the CPU.
[b]Note:[/b] While Ogg Theora videos can also have an [code].ogg[/code] extension, you will have to rename the extension to [code].ogv[/code] to use those videos within Godot.
</description>
<tutorials>
@@ -24,6 +24,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 2f6faec8ec..4f5ae4afb0 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -108,7 +108,7 @@ void VideoStreamPlaybackTheora::video_write() {
Ref<Image> img = memnew(Image(size.x, size.y, 0, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation
- texture->update(img); //zero copy send to visual server
+ texture->update(img); //zero copy send to rendering server
frames_pending = 1;
}
@@ -603,7 +603,7 @@ float VideoStreamPlaybackTheora::get_playback_position() const {
};
void VideoStreamPlaybackTheora::seek(float p_time) {
- WARN_PRINT_ONCE("Seeking in Theora and WebM videos is not implemented yet (it's only supported for GDNative-provided video streams).");
+ WARN_PRINT_ONCE("Seeking in Theora videos is not implemented yet (it's only supported for GDNative-provided video streams).");
}
void VideoStreamPlaybackTheora::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
@@ -673,7 +673,7 @@ void VideoStreamTheora::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamTheora::set_file);
ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamTheora::get_file);
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
}
////////////
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index 30bde96fb4..bf9242cc16 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -20,6 +20,9 @@ env_tinyexr.Prepend(CPPPATH=[thirdparty_dir])
# Enable threaded loading with C++11.
env_tinyexr.Append(CPPDEFINES=["TINYEXR_USE_THREAD"])
+# miniz is an external dependency, we could add it but we can instead rely
+# on our existing bundled zlib.
+env_tinyexr.Append(CPPDEFINES=[("TINYEXR_USE_MINIZ", 0)])
env_thirdparty = env_tinyexr.Clone()
env_thirdparty.disable_warnings()
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index eb7a8597e6..6c4c06aab0 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -33,6 +33,8 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include <zlib.h> // Should come before including tinyexr.
+
#include "thirdparty/tinyexr/tinyexr.h"
Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 6a2fb0f666..f64acf8395 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -31,6 +31,8 @@
#include "image_saver_tinyexr.h"
#include "core/math/math_funcs.h"
+#include <zlib.h> // Should come before including tinyexr.
+
#include "thirdparty/tinyexr/tinyexr.h"
static bool is_supported_format(Image::Format p_format) {
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
index b2fed0cb23..4b385b820d 100644
--- a/modules/upnp/SCsub
+++ b/modules/upnp/SCsub
@@ -26,9 +26,9 @@ if env["builtin_miniupnpc"]:
"receivedata.c",
"addr_is_reserved.c",
]
- thirdparty_sources = [thirdparty_dir + "miniupnpc/" + file for file in thirdparty_sources]
+ thirdparty_sources = [thirdparty_dir + "src/" + file for file in thirdparty_sources]
- env_upnp.Prepend(CPPPATH=[thirdparty_dir])
+ env_upnp.Prepend(CPPPATH=[thirdparty_dir + "include"])
env_upnp.Append(CPPDEFINES=["MINIUPNP_STATICLIB"])
env_upnp.Append(CPPDEFINES=["MINIUPNPC_SET_SOCKET_TIMEOUT"])
diff --git a/modules/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
index 5b1d9dbfd1..2cd0b8843a 100644
--- a/modules/upnp/doc_classes/UPNP.xml
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -16,6 +16,38 @@
[codeblock]
upnp.delete_port_mapping(port)
[/codeblock]
+ [b]Note:[/b] UPnP discovery blocks the current thread. To perform discovery without blocking the main thread, use [Thread]s like this:
+ [codeblock]
+ # Emitted when UPnP port mapping setup is completed (regardless of success or failure).
+ signal upnp_completed(error)
+
+ # Replace this with your own server port number between 1025 and 65535.
+ const SERVER_PORT = 3928
+ var thread = null
+
+ func _upnp_setup(server_port):
+ # UPNP queries take some time.
+ var upnp = UPNP.new()
+ var err = upnp.discover()
+
+ if err != OK:
+ push_error(str(err))
+ emit_signal("upnp_completed", err)
+ return
+
+ if upnp.get_gateway() and upnp.get_gateway().is_valid_gateway():
+ upnp.add_port_mapping(server_port, server_port, ProjectSettings.get_setting("application/config/name"), "UDP")
+ upnp.add_port_mapping(server_port, server_port, ProjectSettings.get_setting("application/config/name"), "TCP")
+ emit_signal("upnp_completed", OK)
+
+ func _ready():
+ thread = Thread.new()
+ thread.start(self, "_upnp_setup", SERVER_PORT)
+
+ func _exit_tree():
+ # Wait for thread finish here to handle game exit while the thread is running.
+ thread.wait_to_finish()
+ [/codeblock]
</description>
<tutorials>
</tutorials>
diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp
index efe618012a..64823deaba 100644
--- a/modules/upnp/upnp.cpp
+++ b/modules/upnp/upnp.cpp
@@ -30,17 +30,17 @@
#include "upnp.h"
-#include <miniupnpc/miniwget.h>
-#include <miniupnpc/upnpcommands.h>
+#include <miniwget.h>
+#include <upnpcommands.h>
#include <stdlib.h>
bool UPNP::is_common_device(const String &dev) const {
return dev.is_empty() ||
- dev.find("InternetGatewayDevice") >= 0 ||
- dev.find("WANIPConnection") >= 0 ||
- dev.find("WANPPPConnection") >= 0 ||
- dev.find("rootdevice") >= 0;
+ dev.find("InternetGatewayDevice") >= 0 ||
+ dev.find("WANIPConnection") >= 0 ||
+ dev.find("WANPPPConnection") >= 0 ||
+ dev.find("rootdevice") >= 0;
}
int UPNP::discover(int timeout, int ttl, const String &device_filter) {
@@ -257,7 +257,7 @@ void UPNP::set_device(int index, Ref<UPNPDevice> device) {
void UPNP::remove_device(int index) {
ERR_FAIL_INDEX(index, devices.size());
- devices.remove(index);
+ devices.remove_at(index);
}
void UPNP::clear_devices() {
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
index b961a9667f..67df187f8c 100644
--- a/modules/upnp/upnp.h
+++ b/modules/upnp/upnp.h
@@ -35,7 +35,7 @@
#include "upnp_device.h"
-#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc.h>
class UPNP : public RefCounted {
GDCLASS(UPNP, RefCounted);
diff --git a/modules/upnp/upnp_device.cpp b/modules/upnp/upnp_device.cpp
index ddc66d593c..692a0f3509 100644
--- a/modules/upnp/upnp_device.cpp
+++ b/modules/upnp/upnp_device.cpp
@@ -32,7 +32,7 @@
#include "upnp.h"
-#include <miniupnpc/upnpcommands.h>
+#include <upnpcommands.h>
String UPNPDevice::query_external_address() const {
ERR_FAIL_COND_V_MSG(!is_valid_gateway(), "", "The Internet Gateway Device must be valid.");
diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp
index 2b48e94604..54240e66fc 100644
--- a/modules/vhacd/register_types.cpp
+++ b/modules/vhacd/register_types.cpp
@@ -32,48 +32,55 @@
#include "scene/resources/mesh.h"
#include "thirdparty/vhacd/public/VHACD.h"
-static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int p_max_convex_hulls = -1) {
- Vector<real_t> vertices;
- vertices.resize(p_faces.size() * 9);
- Vector<uint32_t> indices;
- indices.resize(p_faces.size() * 3);
-
- for (int i = 0; i < p_faces.size(); i++) {
- for (int j = 0; j < 3; j++) {
- vertices.write[i * 9 + j * 3 + 0] = p_faces[i].vertex[j].x;
- vertices.write[i * 9 + j * 3 + 1] = p_faces[i].vertex[j].y;
- vertices.write[i * 9 + j * 3 + 2] = p_faces[i].vertex[j].z;
- indices.write[i * 3 + j] = i * 3 + j;
- }
- }
-
+static Vector<Vector<Vector3>> convex_decompose(const real_t *p_vertices, int p_vertex_count, const uint32_t *p_triangles, int p_triangle_count, const Mesh::ConvexDecompositionSettings &p_settings, Vector<Vector<uint32_t>> *r_convex_indices) {
VHACD::IVHACD::Parameters params;
- if (p_max_convex_hulls > 0) {
- params.m_maxConvexHulls = p_max_convex_hulls;
- }
+ params.m_concavity = p_settings.max_concavity;
+ params.m_alpha = p_settings.symmetry_planes_clipping_bias;
+ params.m_beta = p_settings.revolution_axes_clipping_bias;
+ params.m_minVolumePerCH = p_settings.min_volume_per_convex_hull;
+ params.m_resolution = p_settings.resolution;
+ params.m_maxNumVerticesPerCH = p_settings.max_num_vertices_per_convex_hull;
+ params.m_planeDownsampling = p_settings.plane_downsampling;
+ params.m_convexhullDownsampling = p_settings.convexhull_downsampling;
+ params.m_pca = p_settings.normalize_mesh;
+ params.m_mode = p_settings.mode;
+ params.m_convexhullApproximation = p_settings.convexhull_approximation;
+ params.m_oclAcceleration = true;
+ params.m_maxConvexHulls = p_settings.max_convex_hulls;
+ params.m_projectHullVertices = p_settings.project_hull_vertices;
VHACD::IVHACD *decomposer = VHACD::CreateVHACD();
- decomposer->Compute(vertices.ptr(), vertices.size() / 3, indices.ptr(), indices.size() / 3, params);
+ decomposer->Compute(p_vertices, p_vertex_count, p_triangles, p_triangle_count, params);
int hull_count = decomposer->GetNConvexHulls();
- Vector<Vector<Face3>> ret;
+ Vector<Vector<Vector3>> ret;
+ ret.resize(hull_count);
+
+ if (r_convex_indices) {
+ r_convex_indices->resize(hull_count);
+ }
for (int i = 0; i < hull_count; i++) {
- Vector<Face3> triangles;
VHACD::IVHACD::ConvexHull hull;
decomposer->GetConvexHull(i, hull);
- triangles.resize(hull.m_nTriangles);
- for (uint32_t j = 0; j < hull.m_nTriangles; j++) {
- Face3 f;
+
+ Vector<Vector3> &points = ret.write[i];
+ points.resize(hull.m_nPoints);
+
+ Vector3 *w = points.ptrw();
+ for (uint32_t j = 0; j < hull.m_nPoints; ++j) {
for (int k = 0; k < 3; k++) {
- for (int l = 0; l < 3; l++) {
- f.vertex[k][l] = hull.m_points[hull.m_triangles[j * 3 + k] * 3 + l];
- }
+ w[j][k] = hull.m_points[j * 3 + k];
}
- triangles.write[j] = f;
}
- ret.push_back(triangles);
+
+ if (r_convex_indices) {
+ Vector<uint32_t> &indices = r_convex_indices->write[i];
+ indices.resize(hull.m_nTriangles * 3);
+
+ memcpy(indices.ptrw(), hull.m_triangles, hull.m_nTriangles * 3 * sizeof(uint32_t));
+ }
}
decomposer->Clean();
@@ -83,9 +90,9 @@ static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces, int
}
void register_vhacd_types() {
- Mesh::convex_composition_function = convex_decompose;
+ Mesh::convex_decomposition_function = convex_decompose;
}
void unregister_vhacd_types() {
- Mesh::convex_composition_function = nullptr;
+ Mesh::convex_decomposition_function = nullptr;
}
diff --git a/modules/visual_script/SCsub b/modules/visual_script/SCsub
index 16faea08d7..b91cceae09 100644
--- a/modules/visual_script/SCsub
+++ b/modules/visual_script/SCsub
@@ -6,3 +6,6 @@ Import("env_modules")
env_vs = env_modules.Clone()
env_vs.add_source_files(env.modules_sources, "*.cpp")
+
+if env["tools"]:
+ env_vs.add_source_files(env.modules_sources, "editor/*.cpp")
diff --git a/modules/visual_script/config.py b/modules/visual_script/config.py
index b15479797c..e8990c43c8 100644
--- a/modules/visual_script/config.py
+++ b/modules/visual_script/config.py
@@ -17,6 +17,7 @@ def get_doc_classes():
"VisualScriptConstant",
"VisualScriptConstructor",
"VisualScriptCustomNode",
+ "VisualScriptCustomNodes",
"VisualScriptDeconstruct",
"VisualScriptEditor",
"VisualScriptEmitSignal",
diff --git a/modules/visual_script/doc_classes/VisualScript.xml b/modules/visual_script/doc_classes/VisualScript.xml
index 2327fc0009..a452974014 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -9,7 +9,7 @@
You are most likely to use this class via the Visual Script editor or when writing plugins for it.
</description>
<tutorials>
- <link title="VisualScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/visual_script/index.html</link>
+ <link title="VisualScript documentation index">$DOCS_URL/tutorials/scripting/visual_script/index.html</link>
</tutorials>
<methods>
<method name="add_custom_signal">
@@ -354,6 +354,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
index 4d07f878a2..ed5b814bb7 100644
--- a/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type" default="0">
The type to get the constant from.
@@ -18,6 +16,4 @@
The name of the constant to return.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index 195d766c1d..b3fd678379 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -9,8 +9,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="function" type="int" setter="set_func" getter="get_func" enum="VisualScriptBuiltinFunc.BuiltinFunc" default="0">
The function to be executed.
@@ -114,39 +112,39 @@
<constant name="MATH_RANDF" value="32" enum="BuiltinFunc">
Return a random floating-point value between 0 and 1. To obtain a random value between 0 to N, you can use it with multiplication.
</constant>
- <constant name="MATH_RANDF_RANGE" value="33" enum="BuiltinFunc">
+ <constant name="MATH_RANDI_RANGE" value="33" enum="BuiltinFunc">
+ Return a random 32-bit integer value between the two inputs.
+ </constant>
+ <constant name="MATH_RANDF_RANGE" value="34" enum="BuiltinFunc">
Return a random floating-point value between the two inputs.
</constant>
- <constant name="MATH_RANDI_RANGE" value="34" enum="BuiltinFunc">
- Return a random 32-bit integer value between the two inputs.
+ <constant name="MATH_RANDFN" value="35" enum="BuiltinFunc">
+ Returns a normally-distributed pseudo-random number, using Box-Muller transform with the specified mean and a standard deviation. This is also called Gaussian distribution.
</constant>
- <constant name="MATH_SEED" value="35" enum="BuiltinFunc">
+ <constant name="MATH_SEED" value="36" enum="BuiltinFunc">
Set the seed for the random number generator.
</constant>
- <constant name="MATH_RANDSEED" value="36" enum="BuiltinFunc">
+ <constant name="MATH_RANDSEED" value="37" enum="BuiltinFunc">
Return a random value from the given seed, along with the new seed.
</constant>
- <constant name="MATH_DEG2RAD" value="37" enum="BuiltinFunc">
+ <constant name="MATH_DEG2RAD" value="38" enum="BuiltinFunc">
Convert the input from degrees to radians.
</constant>
- <constant name="MATH_RAD2DEG" value="38" enum="BuiltinFunc">
+ <constant name="MATH_RAD2DEG" value="39" enum="BuiltinFunc">
Convert the input from radians to degrees.
</constant>
- <constant name="MATH_LINEAR2DB" value="39" enum="BuiltinFunc">
+ <constant name="MATH_LINEAR2DB" value="40" enum="BuiltinFunc">
Convert the input from linear volume to decibel volume.
</constant>
- <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc">
+ <constant name="MATH_DB2LINEAR" value="41" enum="BuiltinFunc">
Convert the input from decibel volume to linear volume.
</constant>
- <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc">
- Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis).
+ <constant name="MATH_WRAP" value="42" enum="BuiltinFunc">
</constant>
- <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc">
- Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle).
+ <constant name="MATH_WRAPF" value="43" enum="BuiltinFunc">
</constant>
- <constant name="MATH_WRAP" value="43" enum="BuiltinFunc">
- </constant>
- <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc">
+ <constant name="MATH_PINGPONG" value="44" enum="BuiltinFunc">
+ Return the [code]value[/code] wrapped between [code]0[/code] and the [code]length[/code]. If the limit is reached, the next value the function returned is decreased to the [code]0[/code] side or increased to the [code]length[/code] side (like a triangle wave). If [code]length[/code] is less than zero, it becomes positive.
</constant>
<constant name="LOGIC_MAX" value="45" enum="BuiltinFunc">
Return the greater of the two numbers, also known as their maximum.
@@ -187,32 +185,34 @@
<constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc">
Print the given string to the standard output, without adding a newline.
</constant>
- <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc">
+ <constant name="TEXT_PRINT_VERBOSE" value="58" enum="BuiltinFunc">
+ </constant>
+ <constant name="VAR_TO_STR" value="59" enum="BuiltinFunc">
Serialize a [Variant] to a string.
</constant>
- <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc">
+ <constant name="STR_TO_VAR" value="60" enum="BuiltinFunc">
Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR].
</constant>
- <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc">
+ <constant name="VAR_TO_BYTES" value="61" enum="BuiltinFunc">
Serialize a [Variant] to a [PackedByteArray].
</constant>
- <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc">
+ <constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc">
Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES].
</constant>
- <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc">
+ <constant name="MATH_SMOOTHSTEP" value="63" enum="BuiltinFunc">
Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula:
[codeblock]
var t = clamp((weight - from) / (to - from), 0.0, 1.0)
return t * t * (3.0 - 2.0 * t)
[/codeblock]
</constant>
- <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc">
+ <constant name="MATH_POSMOD" value="64" enum="BuiltinFunc">
</constant>
- <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc">
+ <constant name="MATH_LERP_ANGLE" value="65" enum="BuiltinFunc">
</constant>
- <constant name="TEXT_ORD" value="65" enum="BuiltinFunc">
+ <constant name="TEXT_ORD" value="66" enum="BuiltinFunc">
</constant>
- <constant name="FUNC_MAX" value="66" enum="BuiltinFunc">
+ <constant name="FUNC_MAX" value="67" enum="BuiltinFunc">
Represents the size of the [enum BuiltinFunc] enum.
</constant>
</constants>
diff --git a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
index d6b96957f5..ae32500d2f 100644
--- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
@@ -12,8 +12,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
The constant's parent class.
@@ -22,6 +20,4 @@
The constant to return. See the given class for its available constants.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptComment.xml b/modules/visual_script/doc_classes/VisualScriptComment.xml
index 02cec97b27..5024aae384 100644
--- a/modules/visual_script/doc_classes/VisualScriptComment.xml
+++ b/modules/visual_script/doc_classes/VisualScriptComment.xml
@@ -9,8 +9,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="description" type="String" setter="set_description" getter="get_description" default="&quot;&quot;">
The text inside the comment node.
@@ -22,6 +20,4 @@
The comment node's title.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml
index dec182abf6..ed065759c5 100644
--- a/modules/visual_script/doc_classes/VisualScriptComposeArray.xml
+++ b/modules/visual_script/doc_classes/VisualScriptComposeArray.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptCondition.xml b/modules/visual_script/doc_classes/VisualScriptCondition.xml
index a9981c1f57..a5dd8c7c1b 100644
--- a/modules/visual_script/doc_classes/VisualScriptCondition.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCondition.xml
@@ -15,8 +15,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstant.xml b/modules/visual_script/doc_classes/VisualScriptConstant.xml
index 69676c4bba..388c2bddde 100644
--- a/modules/visual_script/doc_classes/VisualScriptConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptConstant.xml
@@ -12,8 +12,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="type" type="int" setter="set_constant_type" getter="get_constant_type" enum="Variant.Type" default="0">
The constant's type.
@@ -22,6 +20,4 @@
The constant's value.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptConstructor.xml b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
index 4743594ec3..4a3d10aa8e 100644
--- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml
+++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
@@ -32,6 +32,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
index b574576856..2c6313c80a 100644
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
@@ -131,7 +131,7 @@
The [code]inputs[/code] array contains the values of the input ports.
[code]outputs[/code] is an array whose indices should be set to the respective outputs.
The [code]start_mode[/code] is usually [constant START_MODE_BEGIN_SEQUENCE], unless you have used the [code]STEP_*[/code] constants.
- [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node.
+ [code]working_mem[/code] is an array which can be used to persist information between runs of the custom node. The size needs to be predefined using [method _get_working_memory_size].
When returning, you can mask the returned value with one of the [code]STEP_*[/code] constants.
</description>
</method>
diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
new file mode 100644
index 0000000000..1681da7653
--- /dev/null
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualScriptCustomNodes" inherits="Object" version="4.0">
+ <brief_description>
+ Manages custom nodes for the Visual Script editor.
+ </brief_description>
+ <description>
+ This singleton can be used to manage (i.e., add or remove) custom nodes for the Visual Script editor.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="add_custom_node">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="category" type="String" />
+ <argument index="2" name="script" type="Script" />
+ <description>
+ Add a custom Visual Script node to the editor. It'll be placed under "Custom Nodes" with the [code]category[/code] as the parameter.
+ </description>
+ </method>
+ <method name="remove_custom_node">
+ <return type="void" />
+ <argument index="0" name="name" type="String" />
+ <argument index="1" name="category" type="String" />
+ <description>
+ Remove a custom Visual Script node from the editor. Custom nodes already placed on scripts won't be removed.
+ </description>
+ </method>
+ </methods>
+ <signals>
+ <signal name="custom_nodes_updated">
+ <description>
+ Emitted when a custom Visual Script node is added or removed.
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
index 530c80530e..fd9a91c2a5 100644
--- a/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
+++ b/modules/visual_script/doc_classes/VisualScriptDeconstruct.xml
@@ -8,13 +8,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="type" type="int" setter="set_deconstruct_type" getter="get_deconstruct_type" enum="Variant.Type" default="0">
The type to deconstruct.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
index df3121d093..e102e02aa9 100644
--- a/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
+++ b/modules/visual_script/doc_classes/VisualScriptEmitSignal.xml
@@ -12,13 +12,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="signal" type="StringName" setter="set_signal" getter="get_signal" default="&amp;&quot;&quot;">
The signal to emit.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
index 8b7fd3a612..468cae852f 100644
--- a/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
+++ b/modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml
@@ -8,13 +8,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="constant" type="String" setter="set_singleton" getter="get_singleton" default="&quot;&quot;">
The singleton's name.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptExpression.xml b/modules/visual_script/doc_classes/VisualScriptExpression.xml
index 223adbbb96..15e16a15f0 100644
--- a/modules/visual_script/doc_classes/VisualScriptExpression.xml
+++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunction.xml b/modules/visual_script/doc_classes/VisualScriptFunction.xml
index 652418bd64..e0ca9eb280 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
index f0b666e57a..a98cb79106 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="base_script" type="String" setter="set_base_script" getter="get_base_script">
The script to be used when [member call_mode] is set to [constant CALL_MODE_INSTANCE].
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
index 18c3826df8..0d7833446d 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
@@ -32,6 +32,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
index 87fdfd4e53..c6b5b22590 100644
--- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
@@ -8,13 +8,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="constant" type="int" setter="set_global_constant" getter="get_global_constant" default="0">
The constant to be used.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
index b348048298..78fd17c5fc 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
index d7fe7340ad..0e5e832c65 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptInputAction.xml b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
index d6fa111500..eb06d52314 100644
--- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="action" type="StringName" setter="set_action_name" getter="get_action_name" default="&amp;&quot;&quot;">
Name of the action.
diff --git a/modules/visual_script/doc_classes/VisualScriptIterator.xml b/modules/visual_script/doc_classes/VisualScriptIterator.xml
index 1d4ab4daa9..d8305728c6 100644
--- a/modules/visual_script/doc_classes/VisualScriptIterator.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIterator.xml
@@ -15,8 +15,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLists.xml b/modules/visual_script/doc_classes/VisualScriptLists.xml
index d5bff1341a..373e3c7191 100644
--- a/modules/visual_script/doc_classes/VisualScriptLists.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLists.xml
@@ -74,6 +74,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
index 185f0f1ffb..29dbddcdf4 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
@@ -12,8 +12,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0">
The local variable's type.
@@ -22,6 +20,4 @@
The local variable's name.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
index 865f0153c9..96de8ebfdd 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
@@ -14,8 +14,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="type" type="int" setter="set_var_type" getter="get_var_type" enum="Variant.Type" default="0">
The local variable's type.
@@ -24,6 +22,4 @@
The local variable's name.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
index 18a1f030bc..f559083c80 100644
--- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
@@ -12,8 +12,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="constant" type="int" setter="set_math_constant" getter="get_math_constant" enum="VisualScriptMathConstant.MathConstant" default="0">
The math constant.
diff --git a/modules/visual_script/doc_classes/VisualScriptNode.xml b/modules/visual_script/doc_classes/VisualScriptNode.xml
index 23574a5ea8..d080d9eac1 100644
--- a/modules/visual_script/doc_classes/VisualScriptNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptNode.xml
@@ -44,6 +44,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptOperator.xml b/modules/visual_script/doc_classes/VisualScriptOperator.xml
index cbbefa7f71..73d28899f6 100644
--- a/modules/visual_script/doc_classes/VisualScriptOperator.xml
+++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml
@@ -12,8 +12,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="operator" type="int" setter="set_operator" getter="get_operator" enum="Variant.Operator" default="6">
The operation to be performed. See [enum Variant.Operator] for available options.
@@ -22,6 +20,4 @@
The type of the values for this operation. See [enum Variant.Type] for available options.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPreload.xml b/modules/visual_script/doc_classes/VisualScriptPreload.xml
index e11af6c805..e3d60c77bb 100644
--- a/modules/visual_script/doc_classes/VisualScriptPreload.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPreload.xml
@@ -12,13 +12,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="resource" type="Resource" setter="set_preload" getter="get_preload">
The [Resource] to load.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
index c1bf443ea3..e9f30cb605 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="base_script" type="String" setter="set_base_script" getter="get_base_script">
The script to be used when [member set_mode] is set to [constant CALL_MODE_INSTANCE].
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
index 75d6a63469..96261d2c5e 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="assign_op" type="int" setter="set_assign_op" getter="get_assign_op" enum="VisualScriptPropertySet.AssignOp" default="0">
The additional operation to perform when assigning. See [enum AssignOp] for options.
diff --git a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
index ea891be05f..77e97a7219 100644
--- a/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
+++ b/modules/visual_script/doc_classes/VisualScriptResourcePath.xml
@@ -6,12 +6,8 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="path" type="String" setter="set_resource_path" getter="get_resource_path" default="&quot;&quot;">
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptReturn.xml b/modules/visual_script/doc_classes/VisualScriptReturn.xml
index 502628925d..2193f45dc8 100644
--- a/modules/visual_script/doc_classes/VisualScriptReturn.xml
+++ b/modules/visual_script/doc_classes/VisualScriptReturn.xml
@@ -13,8 +13,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="return_enabled" type="bool" setter="set_enable_return_value" getter="is_return_value_enabled" default="false">
If [code]true[/code], the [code]return[/code] input port is available.
@@ -23,6 +21,4 @@
The return value's data type.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
index ffe187a00e..ac672d9b3f 100644
--- a/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSceneNode.xml
@@ -12,13 +12,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="node_path" type="NodePath" setter="set_node_path" getter="get_node_path" default="NodePath(&quot;.&quot;)">
The node's path in the scene tree.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
index 8cddd02c77..fc383593c5 100644
--- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelect.xml b/modules/visual_script/doc_classes/VisualScriptSelect.xml
index 1dbc066e32..d536e623f7 100644
--- a/modules/visual_script/doc_classes/VisualScriptSelect.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSelect.xml
@@ -14,13 +14,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0">
The input variables' type.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSelf.xml b/modules/visual_script/doc_classes/VisualScriptSelf.xml
index bb24f158c1..3c2bd16302 100644
--- a/modules/visual_script/doc_classes/VisualScriptSelf.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSelf.xml
@@ -12,8 +12,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSequence.xml b/modules/visual_script/doc_classes/VisualScriptSequence.xml
index 664722574d..32dcbb9837 100644
--- a/modules/visual_script/doc_classes/VisualScriptSequence.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSequence.xml
@@ -14,13 +14,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="steps" type="int" setter="set_steps" getter="get_steps" default="1">
The number of steps in the sequence.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSubCall.xml b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
index f54887b09c..fdf0e24d3e 100644
--- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
@@ -8,8 +8,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptSwitch.xml b/modules/visual_script/doc_classes/VisualScriptSwitch.xml
index 74504948f0..8e176b56f0 100644
--- a/modules/visual_script/doc_classes/VisualScriptSwitch.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSwitch.xml
@@ -17,8 +17,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
index 5dd1ad3421..ee8e2ad31e 100644
--- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
+++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="base_script" type="String" setter="set_base_script" getter="get_base_script" default="&quot;&quot;">
The target script class to be converted to. If none, only the [member base_type] will be used.
@@ -18,6 +16,4 @@
The target type to be converted to.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
index df20ac53f2..e29765d616 100644
--- a/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptVariableGet.xml
@@ -12,13 +12,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="var_name" type="StringName" setter="set_variable" getter="get_variable" default="&amp;&quot;&quot;">
The variable's name.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
index eb8ebbe338..b2cc70d62e 100644
--- a/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptVariableSet.xml
@@ -13,13 +13,9 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="var_name" type="StringName" setter="set_variable" getter="get_variable" default="&amp;&quot;&quot;">
The variable's name.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptWhile.xml b/modules/visual_script/doc_classes/VisualScriptWhile.xml
index f187957ad2..f090568608 100644
--- a/modules/visual_script/doc_classes/VisualScriptWhile.xml
+++ b/modules/visual_script/doc_classes/VisualScriptWhile.xml
@@ -14,8 +14,4 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptYield.xml b/modules/visual_script/doc_classes/VisualScriptYield.xml
index b04ab7b014..bb7fd8bfb5 100644
--- a/modules/visual_script/doc_classes/VisualScriptYield.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYield.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="mode" type="int" setter="set_yield_mode" getter="get_yield_mode" enum="VisualScriptYield.YieldMode" default="1">
The mode to use for yielding. See [enum YieldMode] for available options.
diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
index c6c3188d08..ad6a7fb4e2 100644
--- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
@@ -8,8 +8,6 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
The base type to be used when [member call_mode] is set to [constant CALL_MODE_INSTANCE].
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index eee9e8f32b..991d258054 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -30,6 +30,10 @@
#include "visual_script_editor.h"
+#include "../visual_script_expression.h"
+#include "../visual_script_flow_control.h"
+#include "../visual_script_func_nodes.h"
+#include "../visual_script_nodes.h"
#include "core/input/input.h"
#include "core/object/class_db.h"
#include "core/object/script_language.h"
@@ -39,10 +43,6 @@
#include "editor/editor_resource_preview.h"
#include "editor/editor_scale.h"
#include "scene/main/window.h"
-#include "visual_script_expression.h"
-#include "visual_script_flow_control.h"
-#include "visual_script_func_nodes.h"
-#include "visual_script_nodes.h"
#ifdef TOOLS_ENABLED
class VisualScriptEditorSignalEdit : public Object {
@@ -779,7 +779,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
for (int i = 0; i < node->get_output_sequence_port_count(); i++) {
Label *text2 = memnew(Label);
text2->set_text(node->get_output_sequence_port_text(i));
- text2->set_align(Label::ALIGN_RIGHT);
+ text2->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
gnode->add_child(text2);
gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
slot_idx++;
@@ -900,7 +900,7 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
if (i < mixed_seq_ports) {
Label *text2 = memnew(Label);
text2->set_text(node->get_output_sequence_port_text(i));
- text2->set_align(Label::ALIGN_RIGHT);
+ text2->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
hbc->add_child(text2);
}
@@ -998,7 +998,7 @@ void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, b
void VisualScriptEditor::_update_node_size(int p_id) {
Node *node = graph->get_node(itos(p_id));
if (Object::cast_to<Control>(node)) {
- Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); // Shrink if text is smaller.
+ Object::cast_to<Control>(node)->reset_size(); // Shrink if text is smaller.
}
}
@@ -1172,9 +1172,9 @@ void VisualScriptEditor::_member_selected() {
if (ti->get_parent() == members->get_root()->get_first_child()) {
#ifdef OSX_ENABLED
- bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_META);
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::META);
#else
- bool held_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
if (held_ctrl) {
ERR_FAIL_COND(!script->has_function(selected));
@@ -1262,6 +1262,23 @@ void VisualScriptEditor::_member_edited() {
undo_redo->create_action(TTR("Rename Variable"));
undo_redo->add_do_method(script.ptr(), "rename_variable", name, new_name);
undo_redo->add_undo_method(script.ptr(), "rename_variable", new_name, name);
+
+ // Also fix all variable setter & getter calls
+ List<int> lst;
+ script->get_node_list(&lst);
+ for (int &P : lst) {
+ Ref<VisualScriptPropertySet> pset = script->get_node(P);
+ if (pset.is_valid() && pset->get_property() == name) {
+ undo_redo->add_do_method(pset.ptr(), "set_property", new_name);
+ undo_redo->add_undo_method(pset.ptr(), "set_property", name);
+ }
+ Ref<VisualScriptPropertyGet> pget = script->get_node(P);
+ if (pget.is_valid() && pget->get_property() == name) {
+ undo_redo->add_do_method(pget.ptr(), "set_property", new_name);
+ undo_redo->add_undo_method(pget.ptr(), "set_property", name);
+ }
+ }
+
undo_redo->add_do_method(this, "_update_members");
undo_redo->add_undo_method(this, "_update_members");
undo_redo->add_do_method(this, "_update_graph");
@@ -1278,6 +1295,18 @@ void VisualScriptEditor::_member_edited() {
undo_redo->create_action(TTR("Rename Signal"));
undo_redo->add_do_method(script.ptr(), "rename_custom_signal", name, new_name);
undo_redo->add_undo_method(script.ptr(), "rename_custom_signal", new_name, name);
+
+ // Also fix all signal emitting nodes
+ List<int> lst;
+ script->get_node_list(&lst);
+ for (int &P : lst) {
+ Ref<VisualScriptEmitSignal> psig = script->get_node(P);
+ if (psig.is_valid() && psig->get_signal() == name) {
+ undo_redo->add_do_method(psig.ptr(), "set_signal", new_name);
+ undo_redo->add_undo_method(psig.ptr(), "set_signal", name);
+ }
+ }
+
undo_redo->add_do_method(this, "_update_members");
undo_redo->add_undo_method(this, "_update_members");
undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
@@ -1299,7 +1328,7 @@ void VisualScriptEditor::_create_function_dialog() {
}
void VisualScriptEditor::_create_function() {
- String name = _validate_name((func_name_box->get_text() == "") ? "new_func" : func_name_box->get_text());
+ String name = _validate_name((func_name_box->get_text().is_empty()) ? "new_func" : func_name_box->get_text());
selected = name;
Vector2 pos = _get_available_pos();
@@ -1477,7 +1506,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
}
} else if (ti->get_parent() == root->get_first_child()) {
selected = ti->get_text(0);
- function_name_edit->set_position(Input::get_singleton()->get_mouse_position() - Vector2(60, -10));
+ function_name_edit->set_position(get_screen_position() + get_local_mouse_position() - Vector2(60, -10));
function_name_edit->popup();
function_name_box->set_text(selected);
function_name_box->select_all();
@@ -1616,7 +1645,7 @@ void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id
Node *node = graph->get_node(itos(p_id));
if (Object::cast_to<Control>(node)) {
- Object::cast_to<Control>(node)->set_size(Vector2(1, 1)); // Shrink if text is smaller.
+ Object::cast_to<Control>(node)->reset_size(); // Shrink if text is smaller.
}
updating_graph = false;
@@ -1676,6 +1705,128 @@ String VisualScriptEditor::_validate_name(const String &p_name) const {
return valid;
}
+void VisualScriptEditor::_on_nodes_copy() {
+ clipboard->nodes.clear();
+ clipboard->data_connections.clear();
+ clipboard->sequence_connections.clear();
+
+ for (int i = 0; i < graph->get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
+ if (gn) {
+ if (gn->is_selected()) {
+ int id = gn->get_name().operator String().to_int();
+ Ref<VisualScriptNode> node = script->get_node(id);
+ if (Object::cast_to<VisualScriptFunction>(*node)) {
+ EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node."));
+ return;
+ }
+ if (node.is_valid()) {
+ clipboard->nodes[id] = node->duplicate(true);
+ clipboard->nodes_positions[id] = script->get_node_position(id);
+ }
+ }
+ }
+ }
+
+ if (clipboard->nodes.is_empty()) {
+ return;
+ }
+
+ List<VisualScript::SequenceConnection> sequence_connections;
+ script->get_sequence_connection_list(&sequence_connections);
+
+ for (const VisualScript::SequenceConnection &E : sequence_connections) {
+ if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
+ clipboard->sequence_connections.insert(E);
+ }
+ }
+
+ List<VisualScript::DataConnection> data_connections;
+ script->get_data_connection_list(&data_connections);
+
+ for (const VisualScript::DataConnection &E : data_connections) {
+ if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
+ clipboard->data_connections.insert(E);
+ }
+ }
+}
+
+void VisualScriptEditor::_on_nodes_paste() {
+ if (clipboard->nodes.is_empty()) {
+ EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!"));
+ return;
+ }
+
+ Map<int, int> remap;
+
+ undo_redo->create_action(TTR("Paste VisualScript Nodes"));
+ int idc = script->get_available_id() + 1;
+
+ Set<int> to_select;
+
+ Set<Vector2> existing_positions;
+
+ {
+ List<int> nodes;
+ script->get_node_list(&nodes);
+ for (int &E : nodes) {
+ Vector2 pos = script->get_node_position(E).snapped(Vector2(2, 2));
+ existing_positions.insert(pos);
+ }
+ }
+
+ bool first_paste = true;
+ Vector2 position_offset = Vector2(0, 0);
+
+ for (KeyValue<int, Ref<VisualScriptNode>> &E : clipboard->nodes) {
+ Ref<VisualScriptNode> node = E.value->duplicate();
+
+ int new_id = idc++;
+ to_select.insert(new_id);
+
+ remap[E.key] = new_id;
+
+ Vector2 paste_pos = clipboard->nodes_positions[E.key];
+
+ if (first_paste) {
+ position_offset = _get_pos_in_graph(mouse_up_position - graph->get_global_position()) - paste_pos;
+ first_paste = false;
+ }
+
+ paste_pos += position_offset;
+
+ while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) {
+ paste_pos += Vector2(20, 20) * EDSCALE;
+ }
+
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos);
+ 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 (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);
+ }
+
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
+
+ undo_redo->commit_action();
+
+ for (int i = 0; i < graph->get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
+ if (gn) {
+ int id = gn->get_name().operator String().to_int();
+ gn->set_selected(to_select.has(id));
+ }
+ }
+}
+
void VisualScriptEditor::_on_nodes_delete() {
// Delete all the selected nodes.
@@ -1823,17 +1974,17 @@ void VisualScriptEditor::input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> key = p_event;
if (key.is_valid() && !key->is_pressed()) {
- mouse_up_position = Input::get_singleton()->get_mouse_position();
+ mouse_up_position = get_screen_position() + get_local_mouse_position();
}
}
void VisualScriptEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_button_mask() == MOUSE_BUTTON_RIGHT) {
+ if (key.is_valid() && key->is_pressed() && key->get_button_mask() == MouseButton::RIGHT) {
saved_position = graph->get_local_mouse_position();
- Point2 gpos = Input::get_singleton()->get_mouse_position();
+ Point2 gpos = get_screen_position() + get_local_mouse_position();
_generic_search(script->get_instance_base_type(), gpos);
}
}
@@ -1927,7 +2078,7 @@ void VisualScriptEditor::_fn_name_box_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == KEY_ENTER) {
+ if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ENTER) {
function_name_edit->hide();
_rename_function(selected, function_name_box->get_text());
function_name_box->clear();
@@ -1943,7 +2094,7 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f
String type = it->get_metadata(0);
- if (type == String()) {
+ if (type.is_empty()) {
return Variant();
}
@@ -1986,7 +2137,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
String(d["type"]) == "nodes")) {
if (String(d["type"]) == "obj_property") {
#ifdef OSX_ENABLED
- const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(KEY_META)));
+ const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Getter. Hold Shift to drop a generic signature."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."));
#endif
@@ -1994,7 +2145,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
if (String(d["type"]) == "nodes") {
#ifdef OSX_ENABLED
- const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(KEY_META)));
+ const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a simple reference to the node."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a simple reference to the node."));
#endif
@@ -2002,7 +2153,7 @@ bool VisualScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &
if (String(d["type"]) == "visual_script_variable_drag") {
#ifdef OSX_ENABLED
- const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(KEY_META)));
+ const_cast<VisualScriptEditor *>(this)->_show_hint(vformat(TTR("Hold %s to drop a Variable Setter."), find_keycode_name(Key::META)));
#else
const_cast<VisualScriptEditor *>(this)->_show_hint(TTR("Hold Ctrl to drop a Variable Setter."));
#endif
@@ -2065,28 +2216,28 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (String(d["type"]) == "visual_script_variable_drag") {
#ifdef OSX_ENABLED
- bool use_set = Input::get_singleton()->is_key_pressed(KEY_META);
+ bool use_set = Input::get_singleton()->is_key_pressed(Key::META);
#else
- bool use_set = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool use_set = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
Vector2 pos = _get_pos_in_graph(p_point);
Ref<VisualScriptNode> vnode;
if (use_set) {
- Ref<VisualScriptVariableSet> vnodes;
- vnodes.instantiate();
- vnodes->set_variable(d["variable"]);
- vnode = vnodes;
+ Ref<VisualScriptPropertySet> pset;
+ pset.instantiate();
+ vnode = pset;
} else {
- Ref<VisualScriptVariableGet> vnodeg;
- vnodeg.instantiate();
- vnodeg->set_variable(d["variable"]);
- vnode = vnodeg;
+ Ref<VisualScriptPropertyGet> pget;
+ pget.instantiate();
+ vnode = pget;
}
int new_id = script->get_available_id();
-
undo_redo->create_action(TTR("Add Node"));
+ undo_redo->add_do_method(vnode.ptr(), "set_property", d["variable"]);
+ undo_redo->add_do_method(vnode.ptr(), "set_base_script", script->get_path());
+
undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
undo_redo->add_do_method(this, "_update_graph");
@@ -2174,9 +2325,9 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (String(d["type"]) == "files") {
#ifdef OSX_ENABLED
- bool use_preload = Input::get_singleton()->is_key_pressed(KEY_META);
+ bool use_preload = Input::get_singleton()->is_key_pressed(Key::META);
#else
- bool use_preload = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool use_preload = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
Vector2 pos = _get_pos_in_graph(p_point);
@@ -2237,9 +2388,9 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
#ifdef OSX_ENABLED
- bool use_node = Input::get_singleton()->is_key_pressed(KEY_META);
+ bool use_node = Input::get_singleton()->is_key_pressed(Key::META);
#else
- bool use_node = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool use_node = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
Array nodes = d["nodes"];
@@ -2287,7 +2438,7 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (String(d["type"]) == "obj_property") {
Node *sn = _find_script_node(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root(), script);
- if (!sn && !Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (!sn && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
EditorNode::get_singleton()->show_warning(vformat(TTR("Can't drop properties because script '%s' is not used in this scene.\nDrop holding 'Shift' to just copy the signature."), get_name()));
return;
}
@@ -2302,12 +2453,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
Vector2 pos = _get_pos_in_graph(p_point);
#ifdef OSX_ENABLED
- bool use_get = Input::get_singleton()->is_key_pressed(KEY_META);
+ bool use_get = Input::get_singleton()->is_key_pressed(Key::META);
#else
- bool use_get = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool use_get = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
- if (!node || Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (!node || Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
if (use_get) {
undo_redo->create_action(TTR("Add Getter Property"));
} else {
@@ -2329,12 +2480,14 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
pget.instantiate();
pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_INSTANCE);
pget->set_base_type(obj->get_class());
-
vnode = pget;
}
undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos);
undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
+ if (!obj->get_script().is_null()) {
+ undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path());
+ }
if (!use_get) {
undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
}
@@ -2365,7 +2518,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
pset->set_base_path(sn->get_path_to(node));
}
-
vnode = pset;
} else {
Ref<VisualScriptPropertyGet> pget;
@@ -2380,9 +2532,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
undo_redo->add_do_method(script.ptr(), "add_node", base_id, vnode, pos);
undo_redo->add_do_method(vnode.ptr(), "set_property", d["property"]);
+ if (!obj->get_script().is_null()) {
+ undo_redo->add_do_method(vnode.ptr(), "set_base_script", Ref<Script>(obj->get_script())->get_path());
+ }
if (!use_get) {
undo_redo->add_do_method(vnode.ptr(), "set_default_input_value", 0, d["value"]);
}
+
undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
undo_redo->add_do_method(this, "_update_graph");
@@ -2460,18 +2616,21 @@ void VisualScriptEditor::reload_text() {
String VisualScriptEditor::get_name() {
String name;
- if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
- name = script->get_path().get_file();
- if (is_unsaved()) {
- if (script->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = script->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in scripts before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (script->is_built_in()) {
+ const String &script_name = script->get_name();
+ if (!script_name.is_empty()) {
+ // If the built-in script has a custom resource name defined,
+ // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
- } else if (script->get_name() != "") {
- name = script->get_name();
- } else {
- name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
@@ -2546,16 +2705,11 @@ void VisualScriptEditor::goto_line(int p_line, bool p_with_error) {
error_line = p_line;
}
- List<StringName> functions;
- script->get_function_list(&functions);
- for (const StringName &E : functions) {
- if (script->has_node(p_line)) {
- _update_graph();
- _update_members();
+ if (script->has_node(p_line)) {
+ _update_graph();
+ _update_members();
- call_deferred(SNAME("call_deferred"), "_center_on_node", E, p_line); //editor might be just created and size might not exist yet
- return;
- }
+ call_deferred(SNAME("call_deferred"), "_center_on_node", p_line); // The editor might be just created and size might not exist yet.
}
}
@@ -2688,7 +2842,7 @@ void VisualScriptEditor::clear_edit_menu() {
void VisualScriptEditor::_change_base_type_callback() {
String bt = select_base_type->get_selected_type();
- ERR_FAIL_COND(bt == String());
+ ERR_FAIL_COND(bt.is_empty());
undo_redo->create_action(TTR("Change Base Type"));
undo_redo->add_do_method(script.ptr(), "set_instance_base_type", bt);
undo_redo->add_undo_method(script.ptr(), "set_instance_base_type", script->get_instance_base_type());
@@ -3059,7 +3213,7 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
if (tg.type == Variant::OBJECT) {
if (tg.script.is_valid()) {
new_connect_node_select->select_from_script(tg.script, "");
- } else if (type_string != String()) {
+ } else if (!type_string.is_empty()) {
new_connect_node_select->select_from_base_type(type_string);
} else {
new_connect_node_select->select_from_base_type(n->get_base_type());
@@ -3083,7 +3237,7 @@ void VisualScriptEditor::_port_action_menu(int p_option) {
property_info = script->get_node(port_action_node)->get_output_value_port_info(port_action_output);
}
if (tg.type == Variant::OBJECT) {
- if (property_info.type == Variant::OBJECT && property_info.hint_string != String()) {
+ if (property_info.type == Variant::OBJECT && !property_info.hint_string.is_empty()) {
new_connect_node_select->select_from_action(property_info.hint_string);
} else {
new_connect_node_select->select_from_action("");
@@ -3308,7 +3462,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
vsfc->set_base_type(base_type);
}
if (p_text == "call" || p_text == "call_deferred") {
@@ -3343,7 +3497,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
vsp->set_base_type(base_type);
}
}
@@ -3372,7 +3526,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
} else if (script->get_node(port_action_node).is_valid()) {
PropertyHint hint = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint;
String base_type = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
- if (base_type != String() && hint == PROPERTY_HINT_TYPE_STRING) {
+ if (!base_type.is_empty() && hint == PROPERTY_HINT_TYPE_STRING) {
vsp->set_base_type(base_type);
}
}
@@ -3422,7 +3576,7 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual
undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, pass_port, new_id);
undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, pass_port, new_id);
} else if (vnode_old->get_output_value_port_info(port_action_output).name == String("return") &&
- !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) {
+ !script->get_output_sequence_ports_connected(port_action_node).has(return_port)) {
undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, return_port, new_id);
undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, return_port, new_id);
} else {
@@ -3550,8 +3704,8 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i
Variant::construct(pinfo.type, existing, &existingp, 1, ce);
}
- default_value_edit->set_position(Object::cast_to<Control>(p_button)->get_global_position() + Vector2(0, Object::cast_to<Control>(p_button)->get_size().y));
- default_value_edit->set_size(Size2(1, 1));
+ default_value_edit->set_position(Object::cast_to<Control>(p_button)->get_screen_position() + Vector2(0, Object::cast_to<Control>(p_button)->get_size().y) * graph->get_zoom());
+ default_value_edit->reset_size();
if (pinfo.type == Variant::NODE_PATH) {
Node *edited_scene = get_tree()->get_edited_scene_root();
@@ -3592,6 +3746,11 @@ void VisualScriptEditor::_hide_timer() {
hint_text->hide();
}
+void VisualScriptEditor::_toggle_scripts_pressed() {
+ ScriptEditor::get_singleton()->toggle_scripts_panel();
+ update_toggle_scripts_button();
+}
+
void VisualScriptEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
@@ -3606,6 +3765,8 @@ void VisualScriptEditor::_notification(int p_what) {
return;
}
+ update_toggle_scripts_button();
+
edit_variable_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
edit_signal_edit->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
func_input_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree")));
@@ -3630,17 +3791,17 @@ void VisualScriptEditor::_notification(int p_what) {
node_colors["constants"] = Color(0.94, 0.18, 0.49);
}
- for (Map<StringName, Color>::Element *E = node_colors.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Color> &E : node_colors) {
const Ref<StyleBoxFlat> sb = tm->get_stylebox(SNAME("frame"), SNAME("GraphNode"));
if (!sb.is_null()) {
Ref<StyleBoxFlat> frame_style = sb->duplicate();
// Adjust the border color to be close to the GraphNode's background color.
// This keeps the node's title area from being too distracting.
- Color color = dark_theme ? E->get().darkened(0.75) : E->get().lightened(0.75);
+ Color color = dark_theme ? E.value.darkened(0.75) : E.value.lightened(0.75);
color.a = 0.9;
frame_style->set_border_color(color);
- node_styles[E->key()] = frame_style;
+ node_styles[E.key] = frame_style;
}
}
@@ -3650,6 +3811,7 @@ void VisualScriptEditor::_notification(int p_what) {
}
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
+ update_toggle_scripts_button();
members_section->set_visible(is_visible_in_tree());
} break;
}
@@ -3699,7 +3861,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_
undo_redo->commit_action();
gn->set_custom_minimum_size(new_size);
- gn->set_size(Size2(1, 1));
+ gn->reset_size();
graph->set_block_minimum_size_adjust(false);
updating_graph = false;
}
@@ -3736,120 +3898,15 @@ void VisualScriptEditor::_menu_option(int p_what) {
case EDIT_FIND_NODE_TYPE: {
_generic_search(script->get_instance_base_type());
} break;
- case EDIT_COPY_NODES:
+ case EDIT_COPY_NODES: {
+ _on_nodes_copy();
+ } break;
case EDIT_CUT_NODES: {
- clipboard->nodes.clear();
- clipboard->data_connections.clear();
- clipboard->sequence_connections.clear();
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- if (gn->is_selected()) {
- int id = gn->get_name().operator String().to_int();
- Ref<VisualScriptNode> node = script->get_node(id);
- if (Object::cast_to<VisualScriptFunction>(*node)) {
- EditorNode::get_singleton()->show_warning(TTR("Can't copy the function node."));
- return;
- }
- if (node.is_valid()) {
- clipboard->nodes[id] = node->duplicate(true);
- clipboard->nodes_positions[id] = script->get_node_position(id);
- }
- }
- }
- }
-
- if (clipboard->nodes.is_empty()) {
- break;
- }
-
- List<VisualScript::SequenceConnection> sequence_connections;
- script->get_sequence_connection_list(&sequence_connections);
-
- for (const VisualScript::SequenceConnection &E : sequence_connections) {
- if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
- clipboard->sequence_connections.insert(E);
- }
- }
-
- List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(&data_connections);
-
- for (const VisualScript::DataConnection &E : data_connections) {
- if (clipboard->nodes.has(E.from_node) && clipboard->nodes.has(E.to_node)) {
- clipboard->data_connections.insert(E);
- }
- }
- if (p_what == EDIT_CUT_NODES) {
- _on_nodes_delete(); // oh yeah, also delete on cut
- }
-
+ _on_nodes_copy();
+ _on_nodes_delete();
} break;
case EDIT_PASTE_NODES: {
- if (clipboard->nodes.is_empty()) {
- EditorNode::get_singleton()->show_warning(TTR("Clipboard is empty!"));
- break;
- }
-
- Map<int, int> remap;
-
- undo_redo->create_action(TTR("Paste VisualScript Nodes"));
- int idc = script->get_available_id() + 1;
-
- Set<int> to_select;
-
- Set<Vector2> existing_positions;
-
- {
- List<int> nodes;
- script->get_node_list(&nodes);
- for (int &E : nodes) {
- Vector2 pos = script->get_node_position(E).snapped(Vector2(2, 2));
- existing_positions.insert(pos);
- }
- }
-
- for (Map<int, Ref<VisualScriptNode>>::Element *E = clipboard->nodes.front(); E; E = E->next()) {
- Ref<VisualScriptNode> node = E->get()->duplicate();
-
- int new_id = idc++;
- to_select.insert(new_id);
-
- remap[E->key()] = new_id;
-
- Vector2 paste_pos = clipboard->nodes_positions[E->key()];
-
- while (existing_positions.has(paste_pos.snapped(Vector2(2, 2)))) {
- paste_pos += Vector2(20, 20) * EDSCALE;
- }
-
- undo_redo->add_do_method(script.ptr(), "add_node", new_id, node, paste_pos);
- 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 (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);
- }
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- undo_redo->commit_action();
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = gn->get_name().operator String().to_int();
- gn->set_selected(to_select.has(id));
- }
- }
+ _on_nodes_paste();
} break;
case EDIT_CREATE_FUNCTION: {
// Create Function.
@@ -3903,16 +3960,16 @@ void VisualScriptEditor::_menu_option(int p_what) {
// the user wants to connect the nodes.
int top_nd = -1;
Vector2 top;
- for (Map<int, Ref<VisualScriptNode>>::Element *E = nodes.front(); E; E = E->next()) {
- Ref<VisualScriptNode> nd = script->get_node(E->key());
+ for (const KeyValue<int, Ref<VisualScriptNode>> &E : nodes) {
+ Ref<VisualScriptNode> nd = script->get_node(E.key);
if (nd.is_valid() && nd->has_input_sequence_port()) {
if (top_nd < 0) {
- top_nd = E->key();
+ top_nd = E.key;
top = script->get_node_position(top_nd);
}
- Vector2 pos = script->get_node_position(E->key());
+ Vector2 pos = script->get_node_position(E.key);
if (top.y > pos.y) {
- top_nd = E->key();
+ top_nd = E.key;
top = pos;
}
}
@@ -4103,11 +4160,11 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
ERR_FAIL_COND(!ti);
member_popup->clear();
- member_popup->set_position(members->get_global_position() + p_pos);
- member_popup->set_size(Vector2());
+ member_popup->set_position(members->get_screen_position() + p_pos);
+ member_popup->reset_size();
- function_name_edit->set_position(members->get_global_position() + p_pos);
- function_name_edit->set_size(Vector2());
+ function_name_edit->set_position(members->get_screen_position() + p_pos);
+ function_name_edit->reset_size();
TreeItem *root = members->get_root();
@@ -4232,6 +4289,15 @@ void VisualScriptEditor::add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_h
void VisualScriptEditor::set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) {
}
+void VisualScriptEditor::update_toggle_scripts_button() {
+ if (is_layout_rtl()) {
+ toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"), SNAME("EditorIcons")));
+ } else {
+ toggle_scripts_button->set_icon(Control::get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons")));
+ }
+ toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text()));
+}
+
void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_move_node", &VisualScriptEditor::_move_node);
ClassDB::bind_method("_update_graph", &VisualScriptEditor::_update_graph, DEFVAL(-1));
@@ -4250,8 +4316,6 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_update_members", &VisualScriptEditor::_update_members);
ClassDB::bind_method("_generic_search", &VisualScriptEditor::_generic_search);
-
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &VisualScriptEditor::add_syntax_highlighter);
}
VisualScriptEditor::VisualScriptEditor() {
@@ -4324,6 +4388,8 @@ VisualScriptEditor::VisualScriptEditor() {
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));
+ graph->connect("copy_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_copy));
+ graph->connect("paste_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_paste));
graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete));
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate));
graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input));
@@ -4333,6 +4399,16 @@ VisualScriptEditor::VisualScriptEditor() {
graph->hide();
graph->connect("scroll_offset_changed", callable_mp(this, &VisualScriptEditor::_graph_ofs_changed));
+ status_bar = memnew(HBoxContainer);
+ add_child(status_bar);
+ status_bar->set_h_size_flags(SIZE_EXPAND_FILL);
+ status_bar->set_custom_minimum_size(Size2(0, 24 * EDSCALE));
+
+ toggle_scripts_button = memnew(Button);
+ toggle_scripts_button->set_flat(true);
+ toggle_scripts_button->connect("pressed", callable_mp(this, &VisualScriptEditor::_toggle_scripts_pressed));
+ status_bar->add_child(toggle_scripts_button);
+
/// Add Buttons to Top Bar/Zoom bar.
HBoxContainer *graph_hbc = graph->get_zoom_hbox();
@@ -4400,8 +4476,8 @@ VisualScriptEditor::VisualScriptEditor() {
select_func_text = memnew(Label);
select_func_text->set_text(TTR("Select or create a function to edit its graph."));
- select_func_text->set_align(Label::ALIGN_CENTER);
- select_func_text->set_valign(Label::VALIGN_CENTER);
+ select_func_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ select_func_text->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
select_func_text->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(select_func_text);
@@ -4409,8 +4485,8 @@ VisualScriptEditor::VisualScriptEditor() {
hint_text->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -100);
hint_text->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
hint_text->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- hint_text->set_align(Label::ALIGN_CENTER);
- hint_text->set_valign(Label::VALIGN_CENTER);
+ hint_text->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ hint_text->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
graph->add_child(hint_text);
hint_text_timer = memnew(Timer);
@@ -4507,11 +4583,11 @@ void VisualScriptEditor::free_clipboard() {
static void register_editor_callback() {
ScriptEditor::register_create_script_editor_function(create_editor);
- ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
- ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KEY_MASK_CMD + KEY_F);
- ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KEY_MASK_CMD + KEY_G);
- ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KEY_MASK_CMD + KEY_R);
- ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KEY_MASK_CMD + KEY_E);
+ ED_SHORTCUT("visual_script_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), Key::F9);
+ ED_SHORTCUT("visual_script_editor/find_node_type", TTR("Find Node Type"), KeyModifierMask::CMD + Key::F);
+ ED_SHORTCUT("visual_script_editor/create_function", TTR("Make Function"), KeyModifierMask::CMD + Key::G);
+ ED_SHORTCUT("visual_script_editor/refresh_nodes", TTR("Refresh Graph"), KeyModifierMask::CMD + Key::R);
+ ED_SHORTCUT("visual_script_editor/edit_member", TTR("Edit Member"), KeyModifierMask::CMD + Key::E);
}
void VisualScriptEditor::register_editor() {
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index 7dfb4fa270..fd1db2bc43 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -31,11 +31,11 @@
#ifndef VISUALSCRIPT_EDITOR_H
#define VISUALSCRIPT_EDITOR_H
+#include "../visual_script.h"
#include "editor/create_dialog.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/property_editor.h"
#include "scene/gui/graph_edit.h"
-#include "visual_script.h"
#include "visual_script_property_selector.h"
class VisualScriptEditorSignalEdit;
@@ -93,6 +93,8 @@ class VisualScriptEditor : public ScriptEditorBase {
ConfirmationDialog *function_create_dialog;
GraphEdit *graph;
+ HBoxContainer *status_bar;
+ Button *toggle_scripts_button;
VisualScriptEditorSignalEdit *signal_editor;
@@ -251,6 +253,8 @@ class VisualScriptEditor : public ScriptEditorBase {
void _node_item_selected();
void _node_item_unselected();
+ void _on_nodes_copy();
+ void _on_nodes_paste();
void _on_nodes_delete();
void _on_nodes_duplicate();
@@ -281,6 +285,8 @@ class VisualScriptEditor : public ScriptEditorBase {
void _member_rmb_selected(const Vector2 &p_pos);
void _member_option(int p_option);
+ void _toggle_scripts_pressed();
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -311,6 +317,8 @@ public:
virtual void tag_saved_version() override;
virtual void reload(bool p_soft) override;
virtual Array get_breakpoints() override;
+ virtual void set_breakpoint(int p_line, bool p_enable) override{};
+ virtual void clear_breakpoints() override{};
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
virtual void update_settings() override;
virtual bool show_members_overview() override;
@@ -328,6 +336,8 @@ public:
static void free_clipboard();
+ void update_toggle_scripts_button() override;
+
VisualScriptEditor();
~VisualScriptEditor();
};
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index d8b88d6cd3..9fa35d4453 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -30,15 +30,15 @@
#include "visual_script_property_selector.h"
+#include "../visual_script.h"
+#include "../visual_script_builtin_funcs.h"
+#include "../visual_script_flow_control.h"
+#include "../visual_script_func_nodes.h"
+#include "../visual_script_nodes.h"
#include "core/os/keyboard.h"
#include "editor/doc_tools.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
-#include "modules/visual_script/visual_script.h"
-#include "modules/visual_script/visual_script_builtin_funcs.h"
-#include "modules/visual_script/visual_script_flow_control.h"
-#include "modules/visual_script/visual_script_func_nodes.h"
-#include "modules/visual_script/visual_script_nodes.h"
#include "scene/main/node.h"
#include "scene/main/window.h"
@@ -51,10 +51,10 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
if (k.is_valid()) {
switch (k->get_keycode()) {
- case KEY_UP:
- case KEY_DOWN:
- case KEY_PAGEUP:
- case KEY_PAGEDOWN: {
+ case Key::UP:
+ case Key::DOWN:
+ case Key::PAGEUP:
+ case Key::PAGEDOWN: {
search_options->gui_input(k);
search_box->accept_event();
@@ -175,7 +175,7 @@ void VisualScriptPropertySelector::_update_search() {
String set_text = set_text_raw.capitalize();
String input = search_box->get_text().capitalize();
- if (input == String() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
+ if (input.is_empty() || get_text_raw.findn(input) != -1 || get_text.findn(input) != -1) {
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, get_text);
item->set_metadata(0, F.name);
@@ -188,7 +188,7 @@ void VisualScriptPropertySelector::_update_search() {
item->set_metadata(2, connecting);
}
- if (input == String() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
+ if (input.is_empty() || set_text_raw.findn(input) != -1 || set_text.findn(input) != -1) {
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, set_text);
item->set_metadata(0, F.name);
@@ -252,7 +252,7 @@ void VisualScriptPropertySelector::_update_search() {
String desc_raw = mi.name + desc_arguments;
String desc = desc_raw.capitalize().replace("( ", "(");
- if (search_box->get_text() != String() &&
+ if (!search_box->get_text().is_empty() &&
name.findn(search_box->get_text()) == -1 &&
desc.findn(search_box->get_text()) == -1 &&
desc_raw.findn(search_box->get_text()) == -1) {
@@ -322,7 +322,7 @@ void VisualScriptPropertySelector::_update_search() {
}
void VisualScriptPropertySelector::create_visualscript_item(const String &name, TreeItem *const root, const String &search_input, const String &text) {
- if (search_input == String() || text.findn(search_input) != -1) {
+ if (search_input.is_empty() || text.findn(search_input) != -1) {
TreeItem *item = search_options->create_item(root);
item->set_text(0, text);
item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
@@ -352,7 +352,7 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
bool in_filter = false;
Vector<String> tx_filters = search_box->get_text().split(" ");
for (int i = 0; i < tx_filters.size(); i++) {
- if (tx_filters[i] == "") {
+ if (tx_filters[i].is_empty()) {
in_filter = true;
} else {
in_filter = false;
@@ -451,7 +451,7 @@ void VisualScriptPropertySelector::_item_selected() {
String at_class = class_type;
- while (at_class != String()) {
+ while (!at_class.is_empty()) {
Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(at_class);
if (E) {
for (int i = 0; i < E->get().properties.size(); i++) {
@@ -465,7 +465,7 @@ void VisualScriptPropertySelector::_item_selected() {
}
at_class = class_type;
- while (at_class != String()) {
+ while (!at_class.is_empty()) {
Map<String, DocData::ClassDoc>::Element *C = dd->class_list.find(at_class);
if (C) {
for (int i = 0; i < C->get().methods.size(); i++) {
@@ -521,7 +521,7 @@ void VisualScriptPropertySelector::_item_selected() {
memdelete(names);
- if (text == String()) {
+ if (text.is_empty()) {
return;
}
diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index 7a87f3d3ee..7a87f3d3ee 100644
--- a/modules/visual_script/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 7fb9707fce..6f56fbbc70 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -34,7 +34,6 @@
#include "core/io/resource_loader.h"
#include "visual_script.h"
#include "visual_script_builtin_funcs.h"
-#include "visual_script_editor.h"
#include "visual_script_expression.h"
#include "visual_script_flow_control.h"
#include "visual_script_func_nodes.h"
@@ -42,7 +41,9 @@
#include "visual_script_yield_nodes.h"
VisualScriptLanguage *visual_script_language = nullptr;
+
#ifdef TOOLS_ENABLED
+#include "editor/visual_script_editor.h"
static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr;
#endif
@@ -117,7 +118,7 @@ void register_visual_script_types() {
GDREGISTER_CLASS(VisualScriptCustomNodes);
ClassDB::set_current_api(ClassDB::API_CORE);
vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes);
- Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", VisualScriptCustomNodes::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptCustomNodes", VisualScriptCustomNodes::get_singleton()));
VisualScriptEditor::register_editor();
#endif
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 142419f90a..63bd9bbee4 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -113,7 +113,7 @@ void VisualScriptNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_default_input_values", "values"), &VisualScriptNode::_set_default_input_values);
ClassDB::bind_method(D_METHOD("_get_default_input_values"), &VisualScriptNode::_get_default_input_values);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_default_input_values", "_get_default_input_values");
ADD_SIGNAL(MethodInfo("ports_changed"));
}
@@ -661,7 +661,7 @@ void VisualScript::custom_signal_remove_argument(const StringName &p_func, int p
ERR_FAIL_COND(instances.size());
ERR_FAIL_COND(!custom_signals.has(p_func));
ERR_FAIL_INDEX(p_argidx, custom_signals[p_func].size());
- custom_signals[p_func].remove(p_argidx);
+ custom_signals[p_func].remove_at(p_argidx);
}
int VisualScript::custom_signal_get_argument_count(const StringName &p_func) const {
@@ -702,8 +702,8 @@ void VisualScript::rename_custom_signal(const StringName &p_name, const StringNa
}
void VisualScript::get_custom_signal_list(List<StringName> *r_custom_signals) const {
- for (const Map<StringName, Vector<Argument>>::Element *E = custom_signals.front(); E; E = E->next()) {
- r_custom_signals->push_back(E->key());
+ for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
+ r_custom_signals->push_back(E.key);
}
r_custom_signals->sort_custom<StringName::AlphCompare>();
@@ -848,13 +848,13 @@ bool VisualScript::has_script_signal(const StringName &p_signal) const {
}
void VisualScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const Map<StringName, Vector<Argument>>::Element *E = custom_signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
MethodInfo mi;
- mi.name = E->key();
- for (int i = 0; i < E->get().size(); i++) {
+ mi.name = E.key;
+ for (int i = 0; i < E.value.size(); i++) {
PropertyInfo arg;
- arg.type = E->get()[i].type;
- arg.name = E->get()[i].name;
+ arg.type = E.value[i].type;
+ arg.name = E.value[i].name;
mi.arguments.push_back(arg);
}
@@ -955,7 +955,7 @@ bool VisualScript::are_subnodes_edited() const {
}
#endif
-const Vector<MultiplayerAPI::RPCConfig> VisualScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> VisualScript::get_rpc_methods() const {
return rpc_functions;
}
@@ -1022,11 +1022,11 @@ void VisualScript::_set_data(const Dictionary &p_data) {
if (functions[E].func_id >= 0 && nodes.has(functions[E].func_id)) {
Ref<VisualScriptFunction> vsf = nodes[functions[E].func_id].node;
if (vsf.is_valid()) {
- if (vsf->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
- MultiplayerAPI::RPCConfig nd;
+ if (vsf->get_rpc_mode() != Multiplayer::RPC_MODE_DISABLED) {
+ Multiplayer::RPCConfig nd;
nd.name = E;
nd.rpc_mode = vsf->get_rpc_mode();
- nd.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; // TODO
+ nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO
if (rpc_functions.find(nd) == -1) {
rpc_functions.push_back(nd);
}
@@ -1036,7 +1036,7 @@ void VisualScript::_set_data(const Dictionary &p_data) {
}
// Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<MultiplayerAPI::SortRPCConfig>();
+ rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
Dictionary VisualScript::_get_data() const {
@@ -1056,13 +1056,13 @@ Dictionary VisualScript::_get_data() const {
d["variables"] = vars;
Array sigs;
- for (const Map<StringName, Vector<Argument>>::Element *E = custom_signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<Argument>> &E : custom_signals) {
Dictionary cs;
- cs["name"] = E->key();
+ cs["name"] = E.key;
Array args;
- for (int i = 0; i < E->get().size(); i++) {
- args.push_back(E->get()[i].name);
- args.push_back(E->get()[i].type);
+ for (int i = 0; i < E.value.size(); i++) {
+ args.push_back(E.value[i].name);
+ args.push_back(E.value[i].type);
}
cs["arguments"] = args;
@@ -1172,7 +1172,7 @@ void VisualScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_data", "data"), &VisualScript::_set_data);
ClassDB::bind_method(D_METHOD("_get_data"), &VisualScript::_get_data);
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id")));
}
@@ -1665,8 +1665,8 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
String err_func = p_method;
int err_line = current_node_id; // Not a line but it works as one.
- if (node && (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD || error_str == String())) {
- if (error_str != String()) {
+ if (node && (r_error.error != Callable::CallError::CALL_ERROR_INVALID_METHOD || error_str.is_empty())) {
+ if (!error_str.is_empty()) {
error_str += " ";
}
@@ -1688,7 +1688,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
// debugger break did not happen
if (!VisualScriptLanguage::singleton->debug_break(error_str, false)) {
- _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), ERR_HANDLER_SCRIPT);
+ _err_print_error(err_func.utf8().get_data(), err_file.utf8().get_data(), err_line, error_str.utf8().get_data(), false, ERR_HANDLER_SCRIPT);
}
//}
@@ -1833,7 +1833,7 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
-const Vector<MultiplayerAPI::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
@@ -2093,8 +2093,8 @@ VisualScriptInstance::~VisualScriptInstance() {
script->instances.erase(owner);
}
- for (Map<int, VisualScriptNodeInstance *>::Element *E = instances.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<int, VisualScriptNodeInstance *> &E : instances) {
+ memdelete(E.value);
}
}
@@ -2370,7 +2370,6 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String
const StringName *f = _call_stack[l].function;
ERR_FAIL_COND(!_call_stack[l].instance->functions.has(*f));
- //VisualScriptInstance::Function *func = &_call_stack[l].instance->functions[*f];
VisualScriptNodeInstance *node = _call_stack[l].instance->instances[*_call_stack[l].current_id];
ERR_FAIL_COND(!node);
@@ -2380,7 +2379,7 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String
for (int i = 0; i < node->input_port_count; i++) {
String name = node->get_base_node()->get_input_value_port_info(i).name;
- if (name == String()) {
+ if (name.is_empty()) {
name = "in_" + itos(i);
}
@@ -2400,7 +2399,7 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String
for (int i = 0; i < node->output_port_count; i++) {
String name = node->get_base_node()->get_output_value_port_info(i).name;
- if (name == String()) {
+ if (name.is_empty()) {
name = "out_" + itos(i);
}
@@ -2416,21 +2415,6 @@ void VisualScriptLanguage::debug_get_stack_level_locals(int p_level, List<String
p_locals->push_back("working_mem/mem_" + itos(i));
p_values->push_back((*_call_stack[l].work_mem)[i]);
}
-
- /*
- ERR_FAIL_INDEX(p_level,_debug_call_stack_pos);
-
-
- VisualFunction *f = _call_stack[l].function;
-
- List<Pair<StringName,int> > locals;
-
- f->debug_get_stack_member_state(*_call_stack[l].line,&locals);
- for( List<Pair<StringName,int> >::Element *E = locals.front();E;E=E->next() ) {
- p_locals->push_back(E->get().first);
- p_values->push_back(_call_stack[l].stack[E->get().second]);
- }
-*/
}
void VisualScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
@@ -2516,8 +2500,8 @@ Ref<VisualScriptNode> VisualScriptLanguage::create_node_from_name(const String &
}
void VisualScriptLanguage::get_registered_node_names(List<String> *r_names) {
- for (Map<String, VisualScriptNodeRegisterFunc>::Element *E = register_funcs.front(); E; E = E->next()) {
- r_names->push_back(E->key());
+ for (const KeyValue<String, VisualScriptNodeRegisterFunc> &E : register_funcs) {
+ r_names->push_back(E.key);
}
}
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 932ebeb27f..39cef8f68b 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -234,7 +234,7 @@ private:
HashMap<StringName, Function> functions;
HashMap<StringName, Variable> variables;
Map<StringName, Vector<Argument>> custom_signals;
- Vector<MultiplayerAPI::RPCConfig> rpc_functions;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
Map<Object *, VisualScriptInstance *> instances;
@@ -362,7 +362,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const override;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
@@ -443,7 +443,7 @@ public:
virtual ScriptLanguage *get_language();
- virtual const Vector<MultiplayerAPI::RPCConfig> get_rpc_methods() const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
VisualScriptInstance();
~VisualScriptInstance();
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index c61c3ae272..7ae85ea415 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -71,18 +71,18 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"randomize",
"randi",
"randf",
- "randf_range",
"randi_range",
+ "randf_range",
+ "randfn",
"seed",
"rand_seed",
"deg2rad",
"rad2deg",
"linear2db",
"db2linear",
- "polar2cartesian",
- "cartesian2polar",
"wrapi",
"wrapf",
+ "pingpong",
"max",
"min",
"clamp",
@@ -96,6 +96,7 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"print",
"printerr",
"printraw",
+ "print_verbose",
"var2str",
"str2var",
"var2bytes",
@@ -131,6 +132,7 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
case TEXT_PRINT:
case TEXT_PRINTERR:
case TEXT_PRINTRAW:
+ case TEXT_PRINT_VERBOSE:
case MATH_SEED:
return true;
default:
@@ -179,6 +181,7 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case TEXT_PRINT:
case TEXT_PRINTERR:
case TEXT_PRINTRAW:
+ case TEXT_PRINT_VERBOSE:
case VAR_TO_STR:
case STR_TO_VAR:
case TYPE_EXISTS:
@@ -189,13 +192,13 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
case MATH_FMOD:
case MATH_FPOSMOD:
case MATH_POSMOD:
+ case MATH_PINGPONG:
case MATH_POW:
case MATH_EASE:
case MATH_SNAPPED:
- case MATH_RANDF_RANGE:
case MATH_RANDI_RANGE:
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR:
+ case MATH_RANDF_RANGE:
+ case MATH_RANDFN:
case LOGIC_MAX:
case LOGIC_MIN:
case TYPE_CONVERT:
@@ -227,6 +230,7 @@ int VisualScriptBuiltinFunc::get_output_value_port_count() const {
case TEXT_PRINT:
case TEXT_PRINTERR:
case TEXT_PRINTRAW:
+ case TEXT_PRINT_VERBOSE:
case MATH_SEED:
return 0;
case MATH_RANDSEED:
@@ -351,6 +355,13 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case MATH_RANDI:
case MATH_RANDF: {
} break;
+ case MATH_RANDI_RANGE: {
+ if (p_idx == 0) {
+ return PropertyInfo(Variant::INT, "from");
+ } else {
+ return PropertyInfo(Variant::INT, "to");
+ }
+ } break;
case MATH_RANDF_RANGE: {
if (p_idx == 0) {
return PropertyInfo(Variant::FLOAT, "from");
@@ -358,11 +369,11 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::FLOAT, "to");
}
} break;
- case MATH_RANDI_RANGE: {
+ case MATH_RANDFN: {
if (p_idx == 0) {
- return PropertyInfo(Variant::INT, "from");
+ return PropertyInfo(Variant::FLOAT, "mean");
} else {
- return PropertyInfo(Variant::INT, "to");
+ return PropertyInfo(Variant::FLOAT, "deviation");
}
} break;
case MATH_SEED:
@@ -381,18 +392,11 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case MATH_DB2LINEAR: {
return PropertyInfo(Variant::FLOAT, "db");
} break;
- case MATH_POLAR2CARTESIAN: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "r");
- } else {
- return PropertyInfo(Variant::FLOAT, "th");
- }
- } break;
- case MATH_CARTESIAN2POLAR: {
+ case MATH_PINGPONG: {
if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "x");
+ return PropertyInfo(Variant::FLOAT, "value");
} else {
- return PropertyInfo(Variant::FLOAT, "y");
+ return PropertyInfo(Variant::FLOAT, "length");
}
} break;
case MATH_WRAP: {
@@ -442,7 +446,8 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case TEXT_STR:
case TEXT_PRINT:
case TEXT_PRINTERR:
- case TEXT_PRINTRAW: {
+ case TEXT_PRINTRAW:
+ case TEXT_PRINT_VERBOSE: {
return PropertyInfo(Variant::NIL, "value");
} break;
case STR_TO_VAR: {
@@ -531,6 +536,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t = Variant::INT;
} break;
case MATH_RANDF:
+ case MATH_RANDFN:
case MATH_RANDF_RANGE: {
t = Variant::FLOAT;
} break;
@@ -550,13 +556,10 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_RAD2DEG:
case MATH_LINEAR2DB:
case MATH_WRAPF:
+ case MATH_PINGPONG:
case MATH_DB2LINEAR: {
t = Variant::FLOAT;
} break;
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR: {
- t = Variant::VECTOR2;
- } break;
case MATH_WRAP: {
t = Variant::INT;
} break;
@@ -594,6 +597,8 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
} break;
case TEXT_PRINTRAW: {
} break;
+ case TEXT_PRINT_VERBOSE: {
+ } break;
case VAR_TO_STR: {
t = Variant::STRING;
} break;
@@ -832,15 +837,20 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
case VisualScriptBuiltinFunc::MATH_RANDF: {
*r_return = Math::randf();
} break;
+ case VisualScriptBuiltinFunc::MATH_RANDI_RANGE: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return = Math::random((int)*p_inputs[0], (int)*p_inputs[1]);
+ } break;
case VisualScriptBuiltinFunc::MATH_RANDF_RANGE: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
*r_return = Math::random((double)*p_inputs[0], (double)*p_inputs[1]);
} break;
- case VisualScriptBuiltinFunc::MATH_RANDI_RANGE: {
+ case VisualScriptBuiltinFunc::MATH_RANDFN: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- *r_return = Math::random((int)*p_inputs[0], (int)*p_inputs[1]);
+ *r_return = Math::randfn((double)*p_inputs[0], (double)*p_inputs[1]);
} break;
case VisualScriptBuiltinFunc::MATH_SEED: {
VALIDATE_ARG_NUM(0);
@@ -874,19 +884,10 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(0);
*r_return = Math::db2linear((double)*p_inputs[0]);
} break;
- case VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN: {
+ case VisualScriptBuiltinFunc::MATH_PINGPONG: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- double r = *p_inputs[0];
- double th = *p_inputs[1];
- *r_return = Vector2(r * Math::cos(th), r * Math::sin(th));
- } break;
- case VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double x = *p_inputs[0];
- double y = *p_inputs[1];
- *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
+ *r_return = Math::pingpong((double)*p_inputs[0], (double)*p_inputs[1]);
} break;
case VisualScriptBuiltinFunc::MATH_WRAP: {
VALIDATE_ARG_NUM(0);
@@ -1056,6 +1057,10 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
OS::get_singleton()->print("%s", str.utf8().get_data());
} break;
+ case VisualScriptBuiltinFunc::TEXT_PRINT_VERBOSE: {
+ String str = *p_inputs[0];
+ print_verbose(str);
+ } break;
case VisualScriptBuiltinFunc::VAR_TO_STR: {
String vars;
VariantWriter::write_to_string(*p_inputs[0], vars);
@@ -1221,18 +1226,18 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
BIND_ENUM_CONSTANT(MATH_RANDI);
BIND_ENUM_CONSTANT(MATH_RANDF);
- BIND_ENUM_CONSTANT(MATH_RANDF_RANGE);
BIND_ENUM_CONSTANT(MATH_RANDI_RANGE);
+ BIND_ENUM_CONSTANT(MATH_RANDF_RANGE);
+ BIND_ENUM_CONSTANT(MATH_RANDFN);
BIND_ENUM_CONSTANT(MATH_SEED);
BIND_ENUM_CONSTANT(MATH_RANDSEED);
BIND_ENUM_CONSTANT(MATH_DEG2RAD);
BIND_ENUM_CONSTANT(MATH_RAD2DEG);
BIND_ENUM_CONSTANT(MATH_LINEAR2DB);
BIND_ENUM_CONSTANT(MATH_DB2LINEAR);
- BIND_ENUM_CONSTANT(MATH_POLAR2CARTESIAN);
- BIND_ENUM_CONSTANT(MATH_CARTESIAN2POLAR);
BIND_ENUM_CONSTANT(MATH_WRAP);
BIND_ENUM_CONSTANT(MATH_WRAPF);
+ BIND_ENUM_CONSTANT(MATH_PINGPONG);
BIND_ENUM_CONSTANT(LOGIC_MAX);
BIND_ENUM_CONSTANT(LOGIC_MIN);
BIND_ENUM_CONSTANT(LOGIC_CLAMP);
@@ -1246,6 +1251,7 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(TEXT_PRINT);
BIND_ENUM_CONSTANT(TEXT_PRINTERR);
BIND_ENUM_CONSTANT(TEXT_PRINTRAW);
+ BIND_ENUM_CONSTANT(TEXT_PRINT_VERBOSE);
BIND_ENUM_CONSTANT(VAR_TO_STR);
BIND_ENUM_CONSTANT(STR_TO_VAR);
BIND_ENUM_CONSTANT(VAR_TO_BYTES);
@@ -1311,8 +1317,9 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF_RANGE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randi_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDI_RANGE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randf_range", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDF_RANGE>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/randfn", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDFN>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/seed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SEED>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randseed", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDSEED>);
@@ -1320,10 +1327,9 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/polar2cartesian", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/cartesian2polar", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/pingpong", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_PINGPONG>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/max", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MAX>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/min", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_MIN>);
@@ -1340,6 +1346,7 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/print", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/printerr", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTERR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/printraw", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINTRAW>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/print_verbose", create_builtin_func_node<VisualScriptBuiltinFunc::TEXT_PRINT_VERBOSE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2str", create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_STR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/str2var", create_builtin_func_node<VisualScriptBuiltinFunc::STR_TO_VAR>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/var2bytes", create_builtin_func_node<VisualScriptBuiltinFunc::VAR_TO_BYTES>);
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index f59a7a0f0c..f71a053f7d 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -71,18 +71,18 @@ public:
MATH_RANDOMIZE,
MATH_RANDI,
MATH_RANDF,
- MATH_RANDF_RANGE,
MATH_RANDI_RANGE,
+ MATH_RANDF_RANGE,
+ MATH_RANDFN,
MATH_SEED,
MATH_RANDSEED,
MATH_DEG2RAD,
MATH_RAD2DEG,
MATH_LINEAR2DB,
MATH_DB2LINEAR,
- MATH_POLAR2CARTESIAN,
- MATH_CARTESIAN2POLAR,
MATH_WRAP,
MATH_WRAPF,
+ MATH_PINGPONG,
LOGIC_MAX,
LOGIC_MIN,
LOGIC_CLAMP,
@@ -96,6 +96,7 @@ public:
TEXT_PRINT,
TEXT_PRINTERR,
TEXT_PRINTRAW,
+ TEXT_PRINT_VERBOSE,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 99b7275008..699042ffa6 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -136,7 +136,7 @@ void VisualScriptExpression::_get_property_list(List<PropertyInfo> *p_list) cons
argt += "," + Variant::get_type_name(Variant::Type(i));
}
- p_list->push_back(PropertyInfo(Variant::STRING, "expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, "expression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
p_list->push_back(PropertyInfo(Variant::INT, "out_type", PROPERTY_HINT_ENUM, argt));
p_list->push_back(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1"));
p_list->push_back(PropertyInfo(Variant::BOOL, "sequenced"));
@@ -1190,7 +1190,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
op->nodes[1] = nullptr;
expression.write[i].is_op = false;
expression.write[i].node = op;
- expression.remove(i + 1);
+ expression.remove_at(i + 1);
}
} else {
@@ -1222,8 +1222,8 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
//replace all 3 nodes by this operator and make it an expression
expression.write[next_op - 1].node = op;
- expression.remove(next_op);
- expression.remove(next_op);
+ expression.remove_at(next_op);
+ expression.remove_at(next_op);
}
}
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 62a4f465cb..278d806595 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -724,7 +724,7 @@ String VisualScriptTypeCast::get_caption() const {
}
String VisualScriptTypeCast::get_text() const {
- if (script != String()) {
+ if (!script.is_empty()) {
return "Is " + script.get_file() + "?";
} else {
return "Is " + base_type + "?";
@@ -762,7 +762,7 @@ String VisualScriptTypeCast::get_base_script() const {
VisualScriptTypeCast::TypeGuess VisualScriptTypeCast::guess_output_type(TypeGuess *p_inputs, int p_output) const {
TypeGuess tg;
tg.type = Variant::OBJECT;
- if (script != String()) {
+ if (!script.is_empty()) {
tg.script = ResourceLoader::load(script);
}
//if (!tg.script.is_valid()) {
@@ -793,7 +793,7 @@ public:
return 0;
}
- if (script != String()) {
+ if (!script.is_empty()) {
Ref<Script> obj_script = obj->get_script();
if (!obj_script.is_valid()) {
return 1; //well, definitely not the script because object we got has no script.
@@ -853,7 +853,7 @@ void VisualScriptTypeCast::_bind_methods() {
String script_ext_hint;
for (const String &E : script_extensions) {
- if (script_ext_hint != String()) {
+ if (!script_ext_hint.is_empty()) {
script_ext_hint += ",";
}
script_ext_hint += "*." + E;
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index 6ba5ad4fd6..651464dfdb 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -370,7 +370,7 @@ void VisualScriptFunctionCall::_update_method_cache() {
} else if (call_mode == CALL_MODE_INSTANCE) {
type = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -514,7 +514,7 @@ Dictionary VisualScriptFunctionCall::_get_argument_cache() const {
void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const {
if (property.name == "base_type") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
@@ -539,7 +539,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
property.hint = PROPERTY_HINT_ENUM;
String sl;
for (const Engine::Singleton &E : names) {
- if (sl != String()) {
+ if (!sl.is_empty()) {
sl += ",";
}
sl += E.name;
@@ -580,7 +580,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
property.hint = PROPERTY_HINT_METHOD_OF_BASE_TYPE;
property.hint_string = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -684,7 +684,7 @@ void VisualScriptFunctionCall::_bind_methods() {
String script_ext_hint;
for (const String &E : script_extensions) {
- if (script_ext_hint != String()) {
+ if (!script_ext_hint.is_empty()) {
script_ext_hint += ",";
}
script_ext_hint += "*." + E;
@@ -696,7 +696,7 @@ void VisualScriptFunctionCall::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "singleton"), "set_singleton", "get_singleton");
ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_argument_cache", "_get_argument_cache");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "argument_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_argument_cache", "_get_argument_cache");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "function"), "set_function", "get_function"); //when set, if loaded properly, will override argument count.
ADD_PROPERTY(PropertyInfo(Variant::INT, "use_default_args"), "set_use_default_args", "get_use_default_args");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "validate"), "set_validate", "get_validate");
@@ -1010,7 +1010,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const
if (index != StringName()) {
detail_prop_name += "." + String(index);
}
- PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, PROPERTY_HINT_TYPE_STRING, E.hint_string);
+ PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, E.hint, E.hint_string);
_adjust_input_index(pinfo);
return pinfo;
}
@@ -1161,7 +1161,7 @@ void VisualScriptPropertySet::_update_cache() {
}
} else if (call_mode == CALL_MODE_INSTANCE) {
type = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -1282,7 +1282,7 @@ VisualScriptPropertySet::AssignOp VisualScriptPropertySet::get_assign_op() const
void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
if (property.name == "base_type") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
@@ -1321,7 +1321,7 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
property.hint_string = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -1361,7 +1361,7 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
property.hint = PROPERTY_HINT_ENUM;
property.hint_string = options;
property.type = Variant::STRING;
- if (options == "") {
+ if (options.is_empty()) {
property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
}
}
@@ -1411,7 +1411,7 @@ void VisualScriptPropertySet::_bind_methods() {
String script_ext_hint;
for (const String &E : script_extensions) {
- if (script_ext_hint != String()) {
+ if (!script_ext_hint.is_empty()) {
script_ext_hint += ",";
}
script_ext_hint += "*." + E;
@@ -1420,7 +1420,7 @@ void VisualScriptPropertySet::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
@@ -1847,7 +1847,7 @@ void VisualScriptPropertyGet::_update_cache() {
}
} else if (call_mode == CALL_MODE_INSTANCE) {
type = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -1988,7 +1988,7 @@ StringName VisualScriptPropertyGet::get_index() const {
void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
if (property.name == "base_type") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
@@ -2027,7 +2027,7 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
property.hint = PROPERTY_HINT_PROPERTY_OF_BASE_TYPE;
property.hint_string = base_type;
- if (base_script != String()) {
+ if (!base_script.is_empty()) {
if (!ResourceCache::has(base_script) && ScriptServer::edit_request_func) {
ScriptServer::edit_request_func(base_script); //make sure it's loaded
}
@@ -2066,7 +2066,7 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
property.hint = PROPERTY_HINT_ENUM;
property.hint_string = options;
property.type = Variant::STRING;
- if (options == "") {
+ if (options.is_empty()) {
property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
}
}
@@ -2113,7 +2113,7 @@ void VisualScriptPropertyGet::_bind_methods() {
String script_ext_hint;
for (const String &E : script_extensions) {
- if (script_ext_hint != String()) {
+ if (!script_ext_hint.is_empty()) {
script_ext_hint += ",";
}
script_ext_hint += "." + E;
@@ -2122,7 +2122,7 @@ void VisualScriptPropertyGet::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "set_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type"), "set_call_mode", "get_call_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_script", PROPERTY_HINT_FILE, script_ext_hint), "set_base_script", "get_base_script");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_type_cache", "_get_type_cache");
ADD_PROPERTY(PropertyInfo(Variant::INT, "basic_type", PROPERTY_HINT_ENUM, bt), "set_basic_type", "get_basic_type");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "node_path", PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE), "set_base_path", "get_base_path");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "property"), "set_property", "get_property");
@@ -2324,7 +2324,7 @@ void VisualScriptEmitSignal::_validate_property(PropertyInfo &property) const {
String ml;
for (const StringName &E : sigs) {
- if (ml != String()) {
+ if (!ml.is_empty()) {
ml += ",";
}
ml += E;
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 44fc91d8f5..bb4c83bd24 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -90,7 +90,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
}
if (p_name == "rpc/mode") {
- rpc_mode = MultiplayerAPI::RPCMode(int(p_value));
+ rpc_mode = Multiplayer::RPCMode(int(p_value));
return true;
}
@@ -253,7 +253,7 @@ String VisualScriptFunction::get_argument_name(int p_argidx) const {
void VisualScriptFunction::remove_argument(int p_argidx) {
ERR_FAIL_INDEX(p_argidx, arguments.size());
- arguments.remove(p_argidx);
+ arguments.remove_at(p_argidx);
ports_changed_notify();
}
@@ -261,11 +261,11 @@ int VisualScriptFunction::get_argument_count() const {
return arguments.size();
}
-void VisualScriptFunction::set_rpc_mode(MultiplayerAPI::RPCMode p_mode) {
+void VisualScriptFunction::set_rpc_mode(Multiplayer::RPCMode p_mode) {
rpc_mode = p_mode;
}
-MultiplayerAPI::RPCMode VisualScriptFunction::get_rpc_mode() const {
+Multiplayer::RPCMode VisualScriptFunction::get_rpc_mode() const {
return rpc_mode;
}
@@ -311,14 +311,14 @@ void VisualScriptFunction::reset_state() {
stack_size = 256;
stack_less = false;
sequenced = true;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ rpc_mode = Multiplayer::RPC_MODE_DISABLED;
}
VisualScriptFunction::VisualScriptFunction() {
stack_size = 256;
stack_less = false;
sequenced = true;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ rpc_mode = Multiplayer::RPC_MODE_DISABLED;
}
void VisualScriptFunction::set_stack_less(bool p_enable) {
@@ -623,7 +623,7 @@ void VisualScriptLists::remove_input_data_port(int p_argidx) {
ERR_FAIL_INDEX(p_argidx, inputports.size());
- inputports.remove(p_argidx);
+ inputports.remove_at(p_argidx);
ports_changed_notify();
notify_property_list_changed();
@@ -679,7 +679,7 @@ void VisualScriptLists::remove_output_data_port(int p_argidx) {
ERR_FAIL_INDEX(p_argidx, outputports.size());
- outputports.remove(p_argidx);
+ outputports.remove_at(p_argidx);
ports_changed_notify();
notify_property_list_changed();
@@ -1307,7 +1307,7 @@ void VisualScriptVariableGet::_validate_property(PropertyInfo &property) const {
String vhint;
for (const StringName &E : vars) {
- if (vhint != String()) {
+ if (!vhint.is_empty()) {
vhint += ",";
}
@@ -1417,7 +1417,7 @@ void VisualScriptVariableSet::_validate_property(PropertyInfo &property) const {
String vhint;
for (const StringName &E : vars) {
- if (vhint != String()) {
+ if (!vhint.is_empty()) {
vhint += ",";
}
@@ -1615,7 +1615,7 @@ PropertyInfo VisualScriptPreload::get_output_value_port_info(int p_idx) const {
pinfo.hint_string = preload->get_class();
if (preload->get_path().is_resource_file()) {
pinfo.name = preload->get_path();
- } else if (preload->get_name() != String()) {
+ } else if (!preload->get_name().is_empty()) {
pinfo.name = preload->get_name();
} else {
pinfo.name = preload->get_class();
@@ -1994,7 +1994,7 @@ void VisualScriptClassConstant::_validate_property(PropertyInfo &property) const
property.hint_string = "";
for (const String &E : constants) {
- if (property.hint_string != String()) {
+ if (!property.hint_string.is_empty()) {
property.hint_string += ",";
}
property.hint_string += E;
@@ -2132,7 +2132,7 @@ void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) c
}
property.hint_string = "";
for (const StringName &E : constants) {
- if (property.hint_string != String()) {
+ if (!property.hint_string.is_empty()) {
property.hint_string += ",";
}
property.hint_string += String(E);
@@ -2363,7 +2363,7 @@ void VisualScriptEngineSingleton::_validate_property(PropertyInfo &property) con
continue; //skip these, too simple named
}
- if (cc != String()) {
+ if (!cc.is_empty()) {
cc += ",";
}
cc += E.name;
@@ -3147,7 +3147,7 @@ String VisualScriptSubCall::get_caption() const {
String VisualScriptSubCall::get_text() const {
Ref<Script> script = get_script();
if (script.is_valid()) {
- if (script->get_name() != String()) {
+ if (!script->get_name().is_empty()) {
return script->get_name();
}
if (script->get_path().is_resource_file()) {
@@ -3415,8 +3415,8 @@ void VisualScriptConstructor::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_constructor", "constructor"), &VisualScriptConstructor::set_constructor);
ClassDB::bind_method(D_METHOD("get_constructor"), &VisualScriptConstructor::get_constructor);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor_type", "get_constructor_type");
- ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "constructor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor", "get_constructor");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor_type", "get_constructor_type");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "constructor", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_constructor", "get_constructor");
}
VisualScriptConstructor::VisualScriptConstructor() {
@@ -3786,7 +3786,7 @@ void VisualScriptInputAction::_validate_property(PropertyInfo &property) const {
al.sort();
for (int i = 0; i < al.size(); i++) {
- if (actions != String()) {
+ if (!actions.is_empty()) {
actions += ",";
}
actions += al[i];
@@ -3958,7 +3958,7 @@ void VisualScriptDeconstruct::_bind_methods() {
}
ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt), "set_deconstruct_type", "get_deconstruct_type");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elem_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_elem_cache", "_get_elem_cache");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "elem_cache", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_elem_cache", "_get_elem_cache");
}
VisualScriptDeconstruct::VisualScriptDeconstruct() {
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index 35d9b0b4fe..78881f0a53 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -49,7 +49,7 @@ class VisualScriptFunction : public VisualScriptNode {
bool stack_less;
int stack_size;
- MultiplayerAPI::RPCMode rpc_mode;
+ Multiplayer::RPCMode rpc_mode;
bool sequenced;
protected:
@@ -90,14 +90,8 @@ public:
void set_stack_size(int p_size);
int get_stack_size() const;
- void set_return_type_enabled(bool p_returns);
- bool is_return_type_enabled() const;
-
- void set_return_type(Variant::Type p_type);
- Variant::Type get_return_type() const;
-
- void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
- MultiplayerAPI::RPCMode get_rpc_mode() const;
+ void set_rpc_mode(Multiplayer::RPCMode p_mode);
+ Multiplayer::RPCMode get_rpc_mode() const;
virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp
index cded1e587c..c5d17e5a11 100644
--- a/modules/visual_script/visual_script_yield_nodes.cpp
+++ b/modules/visual_script/visual_script_yield_nodes.cpp
@@ -121,7 +121,7 @@ public:
ret = STEP_EXIT_FUNCTION_BIT;
break; //return the yield
case VisualScriptYield::YIELD_FRAME:
- state->connect_to_signal(tree, "idle_frame", Array());
+ state->connect_to_signal(tree, "process_frame", Array());
break;
case VisualScriptYield::YIELD_PHYSICS_FRAME:
state->connect_to_signal(tree, "physics_frame", Array());
@@ -186,7 +186,7 @@ void VisualScriptYield::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_wait_time", "sec"), &VisualScriptYield::set_wait_time);
ClassDB::bind_method(D_METHOD("get_wait_time"), &VisualScriptYield::get_wait_time);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NOEDITOR), "set_yield_mode", "get_yield_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Frame,Physics Frame,Time", PROPERTY_USAGE_NO_EDITOR), "set_yield_mode", "get_yield_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time"), "set_wait_time", "get_wait_time");
BIND_ENUM_CONSTANT(YIELD_FRAME);
@@ -415,7 +415,7 @@ VisualScriptYieldSignal::CallMode VisualScriptYieldSignal::get_call_mode() const
void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
if (property.name == "base_type") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = PROPERTY_USAGE_NOEDITOR;
+ property.usage = PROPERTY_USAGE_NO_EDITOR;
}
}
@@ -449,7 +449,7 @@ void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
String ml;
for (const String &E : mstring) {
- if (ml != String()) {
+ if (!ml.is_empty()) {
ml += ",";
}
ml += E;
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index bc31fff066..322314487f 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -3,9 +3,6 @@
Import("env")
Import("env_modules")
-# Only kept to build the thirdparty library used by the theora and webm
-# modules. We now use stb_vorbis for AudioStreamOGGVorbis.
-
env_vorbis = env_modules.Clone()
# Thirdparty source files
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
new file mode 100644
index 0000000000..06ed021a5a
--- /dev/null
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -0,0 +1,435 @@
+/*************************************************************************/
+/* audio_stream_ogg_vorbis.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "audio_stream_ogg_vorbis.h"
+
+#include "core/io/file_access.h"
+#include "core/variant/typed_array.h"
+#include "thirdparty/libogg/ogg/ogg.h"
+
+int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
+ ERR_FAIL_COND_V(!ready, 0);
+ ERR_FAIL_COND_V(!active, 0);
+
+ int todo = p_frames;
+
+ int start_buffer = 0;
+
+ int frames_mixed_this_step = p_frames;
+
+ while (todo && 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;
+ }
+ todo -= mixed;
+ frames_mixed += mixed;
+ start_buffer += mixed;
+ if (!have_packets_left && !have_samples_left) {
+ //end of file!
+ bool is_not_empty = mixed > 0 || vorbis_stream->get_length() > 0;
+ if (vorbis_stream->loop && is_not_empty) {
+ //loop
+
+ seek(vorbis_stream->loop_offset);
+ loops++;
+ // 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;
+}
+
+int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
+ ERR_FAIL_COND_V(!ready, 0);
+ if (!have_samples_left) {
+ ogg_packet *packet = nullptr;
+ int err;
+
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ have_packets_left = false;
+ WARN_PRINT("ran out of packets in stream");
+ return -1;
+ }
+
+ ERR_FAIL_COND_V_MSG((err = vorbis_synthesis(&block, packet)), 0, "Error during vorbis synthesis " + itos(err));
+ ERR_FAIL_COND_V_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), 0, "Error during vorbis block processing " + itos(err));
+
+ have_packets_left = !packet->e_o_s;
+ }
+
+ float **pcm; // Accessed with pcm[channel_idx][sample_idx].
+
+ int frames = vorbis_synthesis_pcmout(&dsp_state, &pcm);
+ if (frames > p_frames) {
+ frames = p_frames;
+ have_samples_left = true;
+ } else {
+ have_samples_left = false;
+ }
+
+ if (info.channels > 1) {
+ for (int frame = 0; frame < frames; frame++) {
+ p_buffer[frame].l = pcm[0][frame];
+ p_buffer[frame].r = pcm[0][frame];
+ }
+ } else {
+ for (int frame = 0; frame < frames; frame++) {
+ p_buffer[frame].l = pcm[0][frame];
+ p_buffer[frame].r = pcm[0][frame];
+ }
+ }
+ vorbis_synthesis_read(&dsp_state, frames);
+ return frames;
+}
+
+float AudioStreamPlaybackOGGVorbis::get_stream_sampling_rate() {
+ return vorbis_data->get_sampling_rate();
+}
+
+bool AudioStreamPlaybackOGGVorbis::_alloc_vorbis() {
+ vorbis_info_init(&info);
+ info_is_allocated = true;
+ vorbis_comment_init(&comment);
+ comment_is_allocated = true;
+
+ ERR_FAIL_COND_V(vorbis_data.is_null(), false);
+ vorbis_data_playback = vorbis_data->instance_playback();
+
+ ogg_packet *packet;
+ int err;
+
+ for (int i = 0; i < 3; i++) {
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ WARN_PRINT("Not enough packets to parse header");
+ return false;
+ }
+
+ err = vorbis_synthesis_headerin(&info, &comment, packet);
+ ERR_FAIL_COND_V_MSG(err != 0, false, "Error parsing header");
+ }
+
+ err = vorbis_synthesis_init(&dsp_state, &info);
+ ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing dsp state");
+ dsp_state_is_allocated = true;
+
+ err = vorbis_block_init(&dsp_state, &block);
+ ERR_FAIL_COND_V_MSG(err != 0, false, "Error initializing block");
+ block_is_allocated = true;
+
+ ready = true;
+
+ return true;
+}
+
+void AudioStreamPlaybackOGGVorbis::start(float p_from_pos) {
+ ERR_FAIL_COND(!ready);
+ active = true;
+ seek(p_from_pos);
+ loops = 0;
+ _begin_resample();
+}
+
+void AudioStreamPlaybackOGGVorbis::stop() {
+ active = false;
+}
+
+bool AudioStreamPlaybackOGGVorbis::is_playing() const {
+ return active;
+}
+
+int AudioStreamPlaybackOGGVorbis::get_loop_count() const {
+ return loops;
+}
+
+float AudioStreamPlaybackOGGVorbis::get_playback_position() const {
+ return float(frames_mixed) / vorbis_data->get_sampling_rate();
+}
+
+void AudioStreamPlaybackOGGVorbis::seek(float p_time) {
+ ERR_FAIL_COND(!ready);
+ ERR_FAIL_COND(vorbis_stream.is_null());
+ if (!active) {
+ return;
+ }
+
+ vorbis_synthesis_restart(&dsp_state);
+
+ if (p_time >= vorbis_stream->get_length()) {
+ p_time = 0;
+ }
+ frames_mixed = uint32_t(vorbis_data->get_sampling_rate() * p_time);
+
+ const int64_t desired_sample = p_time * get_stream_sampling_rate();
+
+ if (!vorbis_data_playback->seek_page(desired_sample)) {
+ WARN_PRINT("seek failed");
+ return;
+ }
+
+ ogg_packet *packet;
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ WARN_PRINT_ONCE("seeking beyond limits");
+ return;
+ }
+
+ // The granule position of the page we're seeking through.
+ int64_t granule_pos = 0;
+
+ int headers_remaining = 0;
+ int samples_in_page = 0;
+ int err;
+ while (true) {
+ if (vorbis_synthesis_idheader(packet)) {
+ headers_remaining = 3;
+ }
+ if (!headers_remaining) {
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err));
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err));
+
+ int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err));
+
+ samples_in_page += samples_out;
+
+ } else {
+ headers_remaining--;
+ }
+ if (packet->granulepos != -1 && headers_remaining == 0) {
+ // This indicates the end of the page.
+ granule_pos = packet->granulepos;
+ break;
+ }
+ if (packet->e_o_s) {
+ break;
+ }
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ // We should get an e_o_s flag before this happens.
+ WARN_PRINT("Vorbis file ended without warning.");
+ break;
+ }
+ }
+
+ int64_t samples_to_burn = samples_in_page - (granule_pos - desired_sample);
+
+ if (samples_to_burn > samples_in_page) {
+ WARN_PRINT("Burning more samples than we have in this page. Check seek algorithm.");
+ } else if (samples_to_burn < 0) {
+ WARN_PRINT("Burning negative samples doesn't make sense. Check seek algorithm.");
+ }
+
+ // Seek again, this time we'll burn a specific number of samples instead of all of them.
+ if (!vorbis_data_playback->seek_page(desired_sample)) {
+ WARN_PRINT("seek failed");
+ return;
+ }
+
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ WARN_PRINT_ONCE("seeking beyond limits");
+ return;
+ }
+ vorbis_synthesis_restart(&dsp_state);
+
+ while (true) {
+ if (vorbis_synthesis_idheader(packet)) {
+ headers_remaining = 3;
+ }
+ if (!headers_remaining) {
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis(&block, packet)), "Error during vorbis synthesis " + itos(err));
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis_blockin(&dsp_state, &block)), "Error during vorbis block processing " + itos(err));
+
+ int samples_out = vorbis_synthesis_pcmout(&dsp_state, nullptr);
+ int read_samples = samples_to_burn > samples_out ? samples_out : samples_to_burn;
+ ERR_FAIL_COND_MSG((err = vorbis_synthesis_read(&dsp_state, samples_out)), "Error during vorbis read updating " + itos(err));
+ samples_to_burn -= read_samples;
+
+ if (samples_to_burn <= 0) {
+ break;
+ }
+ } else {
+ headers_remaining--;
+ }
+ if (packet->granulepos != -1 && headers_remaining == 0) {
+ // This indicates the end of the page.
+ break;
+ }
+ if (packet->e_o_s) {
+ break;
+ }
+ if (!vorbis_data_playback->next_ogg_packet(&packet)) {
+ // We should get an e_o_s flag before this happens.
+ WARN_PRINT("Vorbis file ended without warning.");
+ break;
+ }
+ }
+}
+
+AudioStreamPlaybackOGGVorbis::~AudioStreamPlaybackOGGVorbis() {
+ if (block_is_allocated) {
+ vorbis_block_clear(&block);
+ }
+ if (dsp_state_is_allocated) {
+ vorbis_dsp_clear(&dsp_state);
+ }
+ if (comment_is_allocated) {
+ vorbis_comment_clear(&comment);
+ }
+ if (info_is_allocated) {
+ vorbis_info_clear(&info);
+ }
+}
+
+Ref<AudioStreamPlayback> AudioStreamOGGVorbis::instance_playback() {
+ Ref<AudioStreamPlaybackOGGVorbis> ovs;
+
+ ERR_FAIL_COND_V(packet_sequence.is_null(), nullptr);
+
+ ovs.instantiate();
+ ovs->vorbis_stream = Ref<AudioStreamOGGVorbis>(this);
+ ovs->vorbis_data = packet_sequence;
+ ovs->frames_mixed = 0;
+ ovs->active = false;
+ ovs->loops = 0;
+ if (ovs->_alloc_vorbis()) {
+ return ovs;
+ }
+ // Failed to allocate data structures.
+ return nullptr;
+}
+
+String AudioStreamOGGVorbis::get_stream_name() const {
+ return ""; //return stream_name;
+}
+
+void AudioStreamOGGVorbis::maybe_update_info() {
+ ERR_FAIL_COND(packet_sequence.is_null());
+
+ vorbis_info info;
+ vorbis_comment comment;
+ int err;
+
+ vorbis_info_init(&info);
+ vorbis_comment_init(&comment);
+
+ int packet_count = 0;
+ Ref<OGGPacketSequencePlayback> packet_sequence_playback = packet_sequence->instance_playback();
+
+ for (int i = 0; i < 3; i++) {
+ ogg_packet *packet;
+ if (!packet_sequence_playback->next_ogg_packet(&packet)) {
+ WARN_PRINT("Failed to get header packet");
+ break;
+ }
+ if (i == 0) {
+ packet->b_o_s = 1;
+ }
+
+ if (i == 0) {
+ ERR_FAIL_COND(!vorbis_synthesis_idheader(packet));
+ }
+
+ err = vorbis_synthesis_headerin(&info, &comment, packet);
+ ERR_FAIL_COND_MSG(err != 0, "Error parsing header packet " + itos(i) + ": " + itos(err));
+
+ packet_count++;
+ }
+
+ packet_sequence->set_sampling_rate(info.rate);
+
+ vorbis_comment_clear(&comment);
+ vorbis_info_clear(&info);
+}
+
+void AudioStreamOGGVorbis::set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence) {
+ packet_sequence = p_packet_sequence;
+ if (packet_sequence.is_valid()) {
+ maybe_update_info();
+ }
+}
+
+Ref<OGGPacketSequence> AudioStreamOGGVorbis::get_packet_sequence() const {
+ return packet_sequence;
+}
+
+void AudioStreamOGGVorbis::set_loop(bool p_enable) {
+ loop = p_enable;
+}
+
+bool AudioStreamOGGVorbis::has_loop() const {
+ return loop;
+}
+
+void AudioStreamOGGVorbis::set_loop_offset(float p_seconds) {
+ loop_offset = p_seconds;
+}
+
+float AudioStreamOGGVorbis::get_loop_offset() const {
+ return loop_offset;
+}
+
+float AudioStreamOGGVorbis::get_length() const {
+ ERR_FAIL_COND_V(packet_sequence.is_null(), 0);
+ return packet_sequence->get_length();
+}
+
+bool AudioStreamOGGVorbis::is_monophonic() const {
+ return false;
+}
+
+void AudioStreamOGGVorbis::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_packet_sequence", "packet_sequence"), &AudioStreamOGGVorbis::set_packet_sequence);
+ ClassDB::bind_method(D_METHOD("get_packet_sequence"), &AudioStreamOGGVorbis::get_packet_sequence);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamOGGVorbis::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamOGGVorbis::has_loop);
+
+ ClassDB::bind_method(D_METHOD("set_loop_offset", "seconds"), &AudioStreamOGGVorbis::set_loop_offset);
+ ClassDB::bind_method(D_METHOD("get_loop_offset"), &AudioStreamOGGVorbis::get_loop_offset);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packet_sequence", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_packet_sequence", "get_packet_sequence");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset"), "set_loop_offset", "get_loop_offset");
+}
+
+AudioStreamOGGVorbis::AudioStreamOGGVorbis() {}
+
+AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {}
diff --git a/modules/stb_vorbis/audio_stream_ogg_vorbis.h b/modules/vorbis/audio_stream_ogg_vorbis.h
index 756c241d1f..59a1318a6b 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -28,29 +28,49 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef AUDIO_STREAM_STB_VORBIS_H
-#define AUDIO_STREAM_STB_VORBIS_H
+#ifndef AUDIO_STREAM_LIBVORBIS_H
+#define AUDIO_STREAM_LIBVORBIS_H
-#include "core/io/resource_loader.h"
+#include "core/variant/variant.h"
+#include "modules/ogg/ogg_packet_sequence.h"
#include "servers/audio/audio_stream.h"
-
-#include "thirdparty/misc/stb_vorbis.h"
+#include "thirdparty/libvorbis/vorbis/codec.h"
class AudioStreamOGGVorbis;
class AudioStreamPlaybackOGGVorbis : public AudioStreamPlaybackResampled {
GDCLASS(AudioStreamPlaybackOGGVorbis, AudioStreamPlaybackResampled);
- stb_vorbis *ogg_stream = nullptr;
- stb_vorbis_alloc ogg_alloc;
uint32_t frames_mixed = 0;
bool active = false;
int loops = 0;
+ vorbis_info info;
+ vorbis_comment comment;
+ vorbis_dsp_state dsp_state;
+ vorbis_block block;
+
+ bool info_is_allocated = false;
+ bool comment_is_allocated = false;
+ bool dsp_state_is_allocated = false;
+ bool block_is_allocated = false;
+
+ bool ready = false;
+
+ bool have_samples_left = false;
+ bool have_packets_left = false;
+
friend class AudioStreamOGGVorbis;
+ Ref<OGGPacketSequence> vorbis_data;
+ Ref<OGGPacketSequencePlayback> vorbis_data_playback;
Ref<AudioStreamOGGVorbis> vorbis_stream;
+ int _mix_frames_vorbis(AudioFrame *p_buffer, int p_frames);
+
+ // Allocates vorbis data structures. Returns true upon success, false on failure.
+ bool _alloc_vorbis();
+
protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
@@ -72,20 +92,20 @@ public:
class AudioStreamOGGVorbis : public AudioStream {
GDCLASS(AudioStreamOGGVorbis, AudioStream);
OBJ_SAVE_TYPE(AudioStream); // Saves derived classes with common type so they can be interchanged.
- RES_BASE_EXTENSION("oggstr");
+ RES_BASE_EXTENSION("oggvorbisstr");
friend class AudioStreamPlaybackOGGVorbis;
- void *data = nullptr;
- uint32_t data_len = 0;
-
- int decode_mem_size = 0;
- float sample_rate = 1.0;
int channels = 1;
float length = 0.0;
bool loop = false;
float loop_offset = 0.0;
- void clear_data();
+
+ // Performs a seek to the beginning of the stream, should not be called during playback!
+ // Also causes allocation and deallocation.
+ void maybe_update_info();
+
+ Ref<OGGPacketSequence> packet_sequence;
protected:
static void _bind_methods();
@@ -100,13 +120,15 @@ public:
virtual Ref<AudioStreamPlayback> instance_playback() override;
virtual String get_stream_name() const override;
- void set_data(const Vector<uint8_t> &p_data);
- Vector<uint8_t> get_data() const;
+ void set_packet_sequence(Ref<OGGPacketSequence> p_packet_sequence);
+ Ref<OGGPacketSequence> get_packet_sequence() const;
virtual float get_length() const override; //if supported, otherwise return 0
+ virtual bool is_monophonic() const override;
+
AudioStreamOGGVorbis();
virtual ~AudioStreamOGGVorbis();
};
-#endif
+#endif // AUDIO_STREAM_LIBVORBIS_H
diff --git a/modules/vorbis/config.py b/modules/vorbis/config.py
index 8a384e3066..978eccb29f 100644
--- a/modules/vorbis/config.py
+++ b/modules/vorbis/config.py
@@ -4,3 +4,14 @@ def can_build(env, platform):
def configure(env):
pass
+
+
+def get_doc_classes():
+ return [
+ "AudioStreamOGGVorbis",
+ "AudioStreamPlaybackOGGVorbis",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
index 94fdff5d43..4cd278fe83 100644
--- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
@@ -1,26 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamOGGVorbis" inherits="AudioStream" version="4.0">
<brief_description>
- OGG Vorbis audio stream driver.
</brief_description>
<description>
- OGG Vorbis audio stream driver.
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
- Contains the audio data in bytes.
- </member>
<member name="loop" type="bool" setter="set_loop" getter="has_loop" default="false">
If [code]true[/code], the stream will automatically loop when it reaches the end.
</member>
<member name="loop_offset" type="float" setter="set_loop_offset" getter="get_loop_offset" default="0.0">
Time in seconds at which the stream starts after being looped.
</member>
+ <member name="packet_sequence" type="OGGPacketSequence" setter="set_packet_sequence" getter="get_packet_sequence">
+ Contains the raw OGG data for this stream.
+ </member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml
index b88f5e7e1e..05c70d88da 100644
--- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml
+++ b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml
@@ -1,13 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="MultiplayerPeerGDNative" inherits="MultiplayerPeer" version="4.0">
+<class name="AudioStreamPlaybackOGGVorbis" inherits="AudioStreamPlaybackResampled" version="4.0">
<brief_description>
</brief_description>
<description>
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
</class>
diff --git a/modules/vorbis/register_types.cpp b/modules/vorbis/register_types.cpp
index d3e77ea629..de3f41afdd 100644
--- a/modules/vorbis/register_types.cpp
+++ b/modules/vorbis/register_types.cpp
@@ -30,8 +30,19 @@
#include "register_types.h"
-// Dummy module as libvorbis is needed by other modules (theora ...)
+#include "audio_stream_ogg_vorbis.h"
+#include "resource_importer_ogg_vorbis.h"
-void register_vorbis_types() {}
+void register_vorbis_types() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint()) {
+ Ref<ResourceImporterOGGVorbis> ogg_vorbis_importer;
+ ogg_vorbis_importer.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(ogg_vorbis_importer);
+ }
+#endif
+ GDREGISTER_CLASS(AudioStreamOGGVorbis);
+ GDREGISTER_CLASS(AudioStreamPlaybackOGGVorbis);
+}
void unregister_vorbis_types() {}
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
new file mode 100644
index 0000000000..be9f880103
--- /dev/null
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* resource_importer_ogg_vorbis.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_importer_ogg_vorbis.h"
+
+#include "audio_stream_ogg_vorbis.h"
+#include "core/io/file_access.h"
+#include "core/io/resource_saver.h"
+#include "scene/resources/texture.h"
+#include "thirdparty/libogg/ogg/ogg.h"
+#include "thirdparty/libvorbis/vorbis/codec.h"
+
+String ResourceImporterOGGVorbis::get_importer_name() const {
+ return "oggvorbisstr";
+}
+
+String ResourceImporterOGGVorbis::get_visible_name() const {
+ return "oggvorbisstr";
+}
+
+void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("ogg");
+}
+
+String ResourceImporterOGGVorbis::get_save_extension() const {
+ return "oggvorbisstr";
+}
+
+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 {
+ return true;
+}
+
+int ResourceImporterOGGVorbis::get_preset_count() const {
+ return 0;
+}
+
+String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const {
+ return String();
+}
+
+void ResourceImporterOGGVorbis::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const {
+ r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "loop"), true));
+ 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) {
+ bool loop = p_options["loop"];
+ float loop_offset = p_options["loop_offset"];
+
+ FileAccess *f = FileAccess::open(p_source_file, FileAccess::READ);
+
+ ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
+
+ uint64_t len = f->get_length();
+
+ Vector<uint8_t> file_data;
+ file_data.resize(len);
+ uint8_t *w = file_data.ptrw();
+
+ f->get_buffer(w, len);
+
+ memdelete(f);
+
+ Ref<AudioStreamOGGVorbis> ogg_vorbis_stream;
+ ogg_vorbis_stream.instantiate();
+
+ Ref<OGGPacketSequence> ogg_packet_sequence;
+ ogg_packet_sequence.instantiate();
+
+ ogg_stream_state stream_state;
+ ogg_sync_state sync_state;
+ ogg_page page;
+ ogg_packet packet;
+ bool initialized_stream = false;
+
+ ogg_sync_init(&sync_state);
+ int err;
+ size_t cursor = 0;
+ size_t packet_count = 0;
+ bool done = false;
+ while (!done) {
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ while (ogg_sync_pageout(&sync_state, &page) != 1) {
+ if (cursor >= len) {
+ done = true;
+ break;
+ }
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ ERR_FAIL_COND_V(cursor > len, Error::ERR_INVALID_DATA);
+ size_t copy_size = len - cursor;
+ if (copy_size > OGG_SYNC_BUFFER_SIZE) {
+ copy_size = OGG_SYNC_BUFFER_SIZE;
+ }
+ memcpy(sync_buf, &file_data[cursor], copy_size);
+ ogg_sync_wrote(&sync_state, copy_size);
+ cursor += copy_size;
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+ }
+ if (done) {
+ break;
+ }
+ ERR_FAIL_COND_V_MSG((err = ogg_sync_check(&sync_state)), Error::ERR_INVALID_DATA, "Ogg sync error " + itos(err));
+
+ // Have a page now.
+ if (!initialized_stream) {
+ ogg_stream_init(&stream_state, ogg_page_serialno(&page));
+ ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err));
+ initialized_stream = true;
+ }
+ ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err));
+ ogg_stream_pagein(&stream_state, &page);
+ ERR_FAIL_COND_V_MSG((err = ogg_stream_check(&stream_state)), Error::ERR_INVALID_DATA, "Ogg stream error " + itos(err));
+ int desync_iters = 0;
+
+ Vector<Vector<uint8_t>> packet_data;
+ int64_t granule_pos = 0;
+
+ while (true) {
+ err = ogg_stream_packetout(&stream_state, &packet);
+ if (err == -1) {
+ // According to the docs this is usually recoverable, but don't sit here spinning forever.
+ desync_iters++;
+ ERR_FAIL_COND_V_MSG(desync_iters > 100, Error::ERR_INVALID_DATA, "Packet sync issue during ogg import");
+ continue;
+ } else if (err == 0) {
+ // Not enough data to fully reconstruct a packet. Go on to the next page.
+ break;
+ }
+ if (packet_count == 0 && vorbis_synthesis_idheader(&packet) == 0) {
+ WARN_PRINT("Found a non-vorbis-header packet in a header position");
+ // Clearly this logical stream is not a vorbis stream, so destroy it and try again with the next page.
+ ogg_stream_destroy(&stream_state);
+ initialized_stream = false;
+ break;
+ }
+ granule_pos = packet.granulepos;
+
+ PackedByteArray data;
+ data.resize(packet.bytes);
+ memcpy(data.ptrw(), packet.packet, packet.bytes);
+ packet_data.push_back(data);
+ packet_count++;
+ }
+ if (initialized_stream) {
+ ogg_packet_sequence->push_page(granule_pos, packet_data);
+ }
+ }
+
+ ogg_vorbis_stream->set_packet_sequence(ogg_packet_sequence);
+ ogg_vorbis_stream->set_loop(loop);
+ ogg_vorbis_stream->set_loop_offset(loop_offset);
+
+ return ResourceSaver::save(p_save_path + ".oggvorbisstr", ogg_vorbis_stream);
+}
+
+ResourceImporterOGGVorbis::ResourceImporterOGGVorbis() {
+}
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index 60fe3381fb..8565e0deb8 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -28,31 +28,35 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RESOURCEIMPORTEROGGVORBIS_H
-#define RESOURCEIMPORTEROGGVORBIS_H
+#ifndef RESOURCE_IMPORTER_OGG_VORBIS_H
+#define RESOURCE_IMPORTER_OGG_VORBIS_H
-#include "audio_stream_ogg_vorbis.h"
#include "core/io/resource_importer.h"
class ResourceImporterOGGVorbis : public ResourceImporter {
GDCLASS(ResourceImporterOGGVorbis, ResourceImporter);
+ enum {
+ OGG_SYNC_BUFFER_SIZE = 8192,
+ };
+
+private:
+ // virtual int get_samples_in_packet(Vector<uint8_t> p_packet) = 0;
+
public:
- virtual String get_importer_name() const override;
- virtual String get_visible_name() const override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual String get_save_extension() const override;
virtual String get_resource_type() const override;
-
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
virtual int get_preset_count() const override;
virtual String get_preset_name(int p_idx) const override;
-
- virtual void get_import_options(List<ImportOption> *r_options, int p_preset = 0) const override;
- virtual bool get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) 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 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;
ResourceImporterOGGVorbis();
};
-#endif // RESOURCEIMPORTEROGGVORBIS_H
+#endif // RESOURCE_IMPORTER_OGG_VORBIS_H
diff --git a/modules/webm/SCsub b/modules/webm/SCsub
deleted file mode 100644
index 44e80e2870..0000000000
--- a/modules/webm/SCsub
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_webm = env_modules.Clone()
-
-# Thirdparty source files
-
-thirdparty_obj = []
-
-thirdparty_dir = "#thirdparty/libsimplewebm/"
-thirdparty_sources = [
- "libwebm/mkvparser/mkvparser.cc",
- "OpusVorbisDecoder.cpp",
- "VPXDecoder.cpp",
- "WebMDemuxer.cpp",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env_webm.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "libwebm/"])
-
-# also requires libogg, libvorbis and libopus
-if env["builtin_libogg"]:
- env_webm.Prepend(CPPPATH=["#thirdparty/libogg"])
-if env["builtin_libvorbis"]:
- env_webm.Prepend(CPPPATH=["#thirdparty/libvorbis"])
-if env["builtin_opus"]:
- env_webm.Prepend(CPPPATH=["#thirdparty/opus"])
-
-if env["builtin_libvpx"]:
- env_webm.Prepend(CPPPATH=["#thirdparty/libvpx"])
- SConscript("libvpx/SCsub")
-
-env_thirdparty = env_webm.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
-env.modules_sources += thirdparty_obj
-
-# Godot source files
-
-module_obj = []
-
-env_webm.add_source_files(module_obj, "*.cpp")
-env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/webm/config.py b/modules/webm/config.py
deleted file mode 100644
index 99f8ace114..0000000000
--- a/modules/webm/config.py
+++ /dev/null
@@ -1,19 +0,0 @@
-def can_build(env, platform):
- if platform in ["iphone"]:
- return False
-
- return env.module_check_dependencies("webm", ["ogg", "opus", "vorbis"])
-
-
-def configure(env):
- pass
-
-
-def get_doc_classes():
- return [
- "VideoStreamWebm",
- ]
-
-
-def get_doc_path():
- return "doc_classes"
diff --git a/modules/webm/doc_classes/VideoStreamWebm.xml b/modules/webm/doc_classes/VideoStreamWebm.xml
deleted file mode 100644
index 3b9acfd873..0000000000
--- a/modules/webm/doc_classes/VideoStreamWebm.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VideoStreamWebm" inherits="VideoStream" version="4.0">
- <brief_description>
- [VideoStream] resource for WebM videos.
- </brief_description>
- <description>
- [VideoStream] resource handling the [url=https://www.webmproject.org/]WebM[/url] video format with [code].webm[/code] extension. Both the VP8 and VP9 codecs are supported. The VP8 and VP9 codecs are more efficient than [VideoStreamTheora], but they require more CPU resources to decode (especially VP9). Both the VP8 and VP9 codecs are decoded on the CPU.
- [b]Note:[/b] Alpha channel (also known as transparency) is not supported. The video will always appear to have a black background, even if it originally contains an alpha channel.
- [b]Note:[/b] There are known bugs and performance issues with WebM video playback in Godot. If you run into problems, try using the Ogg Theora format instead: [VideoStreamTheora]
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="get_file">
- <return type="String" />
- <description>
- Returns the WebM video file handled by this [VideoStreamWebm].
- </description>
- </method>
- <method name="set_file">
- <return type="void" />
- <argument index="0" name="file" type="String" />
- <description>
- Sets the WebM video file that this [VideoStreamWebm] resource handles. The [code]file[/code] name should have the [code].webm[/code] extension.
- </description>
- </method>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
deleted file mode 100644
index 4334cf732b..0000000000
--- a/modules/webm/libvpx/SCsub
+++ /dev/null
@@ -1,382 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-# Thirdparty sources
-
-libvpx_dir = "#thirdparty/libvpx/"
-
-libvpx_sources = [
- "vp8/vp8_dx_iface.c",
- "vp8/common/generic/systemdependent.c",
- "vp8/common/alloccommon.c",
- "vp8/common/blockd.c",
- "vp8/common/copy_c.c",
- "vp8/common/debugmodes.c",
- "vp8/common/dequantize.c",
- "vp8/common/entropy.c",
- "vp8/common/entropymode.c",
- "vp8/common/entropymv.c",
- "vp8/common/extend.c",
- "vp8/common/filter.c",
- "vp8/common/findnearmv.c",
- "vp8/common/idct_blk.c",
- "vp8/common/idctllm.c",
- "vp8/common/loopfilter_filters.c",
- "vp8/common/mbpitch.c",
- "vp8/common/modecont.c",
- "vp8/common/quant_common.c",
- "vp8/common/reconinter.c",
- "vp8/common/reconintra.c",
- "vp8/common/reconintra4x4.c",
- "vp8/common/rtcd.c",
- "vp8/common/setupintrarecon.c",
- "vp8/common/swapyv12buffer.c",
- "vp8/common/treecoder.c",
- "vp8/common/vp8_loopfilter.c",
- "vp8/decoder/dboolhuff.c",
- "vp8/decoder/decodeframe.c",
- "vp8/decoder/decodemv.c",
- "vp8/decoder/detokenize.c",
- "vp8/decoder/onyxd_if.c",
- "vp9/vp9_dx_iface.c",
- "vp9/common/vp9_alloccommon.c",
- "vp9/common/vp9_blockd.c",
- "vp9/common/vp9_common_data.c",
- "vp9/common/vp9_debugmodes.c",
- "vp9/common/vp9_entropy.c",
- "vp9/common/vp9_entropymode.c",
- "vp9/common/vp9_entropymv.c",
- "vp9/common/vp9_filter.c",
- "vp9/common/vp9_frame_buffers.c",
- "vp9/common/vp9_idct.c",
- "vp9/common/vp9_loopfilter.c",
- "vp9/common/vp9_mvref_common.c",
- "vp9/common/vp9_pred_common.c",
- "vp9/common/vp9_quant_common.c",
- "vp9/common/vp9_reconinter.c",
- "vp9/common/vp9_reconintra.c",
- "vp9/common/vp9_rtcd.c",
- "vp9/common/vp9_scale.c",
- "vp9/common/vp9_scan.c",
- "vp9/common/vp9_seg_common.c",
- "vp9/common/vp9_thread_common.c",
- "vp9/common/vp9_tile_common.c",
- "vp9/decoder/vp9_decodeframe.c",
- "vp9/decoder/vp9_decodemv.c",
- "vp9/decoder/vp9_decoder.c",
- "vp9/decoder/vp9_detokenize.c",
- "vp9/decoder/vp9_dsubexp.c",
- "vp9/decoder/vp9_dthread.c",
- "vpx/src/vpx_codec.c",
- "vpx/src/vpx_decoder.c",
- "vpx/src/vpx_image.c",
- "vpx/src/vpx_psnr.c",
- "vpx_dsp/bitreader.c",
- "vpx_dsp/bitreader_buffer.c",
- "vpx_dsp/intrapred.c",
- "vpx_dsp/inv_txfm.c",
- "vpx_dsp/loopfilter.c",
- "vpx_dsp/prob.c",
- "vpx_dsp/vpx_convolve.c",
- "vpx_dsp/vpx_dsp_rtcd.c",
- "vpx_mem/vpx_mem.c",
- "vpx_scale/vpx_scale_rtcd.c",
- "vpx_scale/generic/yv12config.c",
- "vpx_scale/generic/yv12extend.c",
- "vpx_util/vpx_thread.c",
-]
-
-libvpx_sources_mt = [
- "vp8/decoder/threading.c",
-]
-
-libvpx_sources_intrin_x86 = [
- "vp8/common/x86/filter_x86.c",
- "vp8/common/x86/loopfilter_x86.c",
- "vp8/common/x86/vp8_asm_stubs.c",
- "vpx_dsp/x86/vpx_asm_stubs.c",
-]
-libvpx_sources_intrin_x86_mmx = [
- "vp8/common/x86/idct_blk_mmx.c",
-]
-libvpx_sources_intrin_x86_sse2 = [
- "vp8/common/x86/idct_blk_sse2.c",
- "vp9/common/x86/vp9_idct_intrin_sse2.c",
- "vpx_dsp/x86/inv_txfm_sse2.c",
- "vpx_dsp/x86/loopfilter_sse2.c",
-]
-libvpx_sources_intrin_x86_ssse3 = [
- "vpx_dsp/x86/vpx_subpixel_8t_intrin_ssse3.c",
-]
-libvpx_sources_intrin_x86_avx2 = [
- "vpx_dsp/x86/loopfilter_avx2.c",
- "vpx_dsp/x86/vpx_subpixel_8t_intrin_avx2.c",
-]
-libvpx_sources_x86asm = [
- "vp8/common/x86/copy_sse2.asm",
- "vp8/common/x86/copy_sse3.asm",
- "vp8/common/x86/dequantize_mmx.asm",
- "vp8/common/x86/idctllm_mmx.asm",
- "vp8/common/x86/idctllm_sse2.asm",
- "vp8/common/x86/iwalsh_mmx.asm",
- "vp8/common/x86/iwalsh_sse2.asm",
- "vp8/common/x86/loopfilter_sse2.asm",
- "vp8/common/x86/recon_mmx.asm",
- "vp8/common/x86/recon_sse2.asm",
- "vp8/common/x86/subpixel_mmx.asm",
- "vp8/common/x86/subpixel_sse2.asm",
- "vp8/common/x86/subpixel_ssse3.asm",
- "vp8/common/x86/vp8_loopfilter_mmx.asm",
- "vpx_dsp/x86/intrapred_sse2.asm",
- "vpx_dsp/x86/intrapred_ssse3.asm",
- "vpx_dsp/x86/inv_wht_sse2.asm",
- "vpx_dsp/x86/vpx_convolve_copy_sse2.asm",
- "vpx_dsp/x86/vpx_subpixel_8t_sse2.asm",
- "vpx_dsp/x86/vpx_subpixel_8t_ssse3.asm",
- "vpx_dsp/x86/vpx_subpixel_bilinear_sse2.asm",
- "vpx_dsp/x86/vpx_subpixel_bilinear_ssse3.asm",
- "vpx_ports/emms.asm",
-]
-libvpx_sources_x86_64asm = [
- "vp8/common/x86/loopfilter_block_sse2_x86_64.asm",
- "vpx_dsp/x86/inv_txfm_ssse3_x86_64.asm",
-]
-
-libvpx_sources_arm = [
- "vpx_ports/arm_cpudetect.c",
- "vp8/common/arm/loopfilter_arm.c",
-]
-libvpx_sources_arm_neon = [
- "vp8/common/arm/neon/bilinearpredict_neon.c",
- "vp8/common/arm/neon/copymem_neon.c",
- "vp8/common/arm/neon/dc_only_idct_add_neon.c",
- "vp8/common/arm/neon/dequant_idct_neon.c",
- "vp8/common/arm/neon/dequantizeb_neon.c",
- "vp8/common/arm/neon/idct_blk_neon.c",
- "vp8/common/arm/neon/idct_dequant_0_2x_neon.c",
- "vp8/common/arm/neon/idct_dequant_full_2x_neon.c",
- "vp8/common/arm/neon/iwalsh_neon.c",
- "vp8/common/arm/neon/loopfiltersimplehorizontaledge_neon.c",
- "vp8/common/arm/neon/loopfiltersimpleverticaledge_neon.c",
- "vp8/common/arm/neon/mbloopfilter_neon.c",
- "vp8/common/arm/neon/shortidct4x4llm_neon.c",
- "vp8/common/arm/neon/sixtappredict_neon.c",
- "vp8/common/arm/neon/vp8_loopfilter_neon.c",
- "vp9/common/arm/neon/vp9_iht4x4_add_neon.c",
- "vp9/common/arm/neon/vp9_iht8x8_add_neon.c",
- "vpx_dsp/arm/idct16x16_1_add_neon.c",
- "vpx_dsp/arm/idct16x16_add_neon.c",
- "vpx_dsp/arm/idct16x16_neon.c",
- "vpx_dsp/arm/idct32x32_1_add_neon.c",
- "vpx_dsp/arm/idct32x32_add_neon.c",
- "vpx_dsp/arm/idct4x4_1_add_neon.c",
- "vpx_dsp/arm/idct4x4_add_neon.c",
- "vpx_dsp/arm/idct8x8_1_add_neon.c",
- "vpx_dsp/arm/idct8x8_add_neon.c",
- "vpx_dsp/arm/intrapred_neon.c",
- "vpx_dsp/arm/loopfilter_16_neon.c",
- "vpx_dsp/arm/loopfilter_4_neon.c",
- "vpx_dsp/arm/loopfilter_8_neon.c",
- "vpx_dsp/arm/loopfilter_neon.c",
- "vpx_dsp/arm/vpx_convolve8_avg_neon.c",
- "vpx_dsp/arm/vpx_convolve8_neon.c",
- "vpx_dsp/arm/vpx_convolve_avg_neon.c",
- "vpx_dsp/arm/vpx_convolve_copy_neon.c",
- "vpx_dsp/arm/vpx_convolve_neon.c",
-]
-libvpx_sources_arm_neon_gas = [
- "vpx_dsp/arm/gas/intrapred_neon_asm.s",
- "vpx_dsp/arm/gas/loopfilter_mb_neon.s",
- "vpx_dsp/arm/gas/save_reg_neon.s",
-]
-libvpx_sources_arm_neon_armasm_ms = [
- "vpx_dsp/arm/armasm_ms/intrapred_neon_asm.asm",
- "vpx_dsp/arm/armasm_ms/loopfilter_mb_neon.asm",
- "vpx_dsp/arm/armasm_ms/save_reg_neon.asm",
-]
-libvpx_sources_arm_neon_gas_apple = [
- "vpx_dsp/arm/gas_apple/intrapred_neon_asm.s",
- "vpx_dsp/arm/gas_apple/loopfilter_mb_neon.s",
- "vpx_dsp/arm/gas_apple/save_reg_neon.s",
-]
-
-libvpx_sources = [libvpx_dir + file for file in libvpx_sources]
-libvpx_sources_mt = [libvpx_dir + file for file in libvpx_sources_mt]
-libvpx_sources_intrin_x86 = [libvpx_dir + file for file in libvpx_sources_intrin_x86]
-libvpx_sources_intrin_x86_mmx = [libvpx_dir + file for file in libvpx_sources_intrin_x86_mmx]
-libvpx_sources_intrin_x86_sse2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_sse2]
-libvpx_sources_intrin_x86_ssse3 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_ssse3]
-libvpx_sources_intrin_x86_avx2 = [libvpx_dir + file for file in libvpx_sources_intrin_x86_avx2]
-libvpx_sources_x86asm = [libvpx_dir + file for file in libvpx_sources_x86asm]
-libvpx_sources_x86_64asm = [libvpx_dir + file for file in libvpx_sources_x86_64asm]
-libvpx_sources_arm = [libvpx_dir + file for file in libvpx_sources_arm]
-libvpx_sources_arm_neon = [libvpx_dir + file for file in libvpx_sources_arm_neon]
-libvpx_sources_arm_neon_gas = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas]
-libvpx_sources_arm_neon_armasm_ms = [libvpx_dir + file for file in libvpx_sources_arm_neon_armasm_ms]
-libvpx_sources_arm_neon_gas_apple = [libvpx_dir + file for file in libvpx_sources_arm_neon_gas_apple]
-
-
-env_libvpx = env_modules.Clone()
-env_libvpx.disable_warnings()
-env_libvpx.Prepend(CPPPATH=[libvpx_dir])
-
-webm_multithread = env["platform"] != "javascript"
-
-cpu_bits = env["bits"]
-webm_cpu_x86 = False
-webm_cpu_arm = False
-if env["platform"] == "uwp":
- if "arm" in env["PROGSUFFIX"]:
- webm_cpu_arm = True
- else:
- webm_cpu_x86 = True
-else:
- import platform
-
- is_x11_or_server_arm = env["platform"] == "linuxbsd" and (
- platform.machine().startswith("arm") or platform.machine().startswith("aarch")
- )
- is_macos_x86 = env["platform"] == "osx" and ("arch" in env and (env["arch"] != "arm64"))
- is_ios_x86 = env["platform"] == "iphone" and ("arch" in env and env["arch"].startswith("x86"))
- is_android_x86 = env["platform"] == "android" and env["android_arch"].startswith("x86")
- if is_android_x86:
- cpu_bits = "32" if env["android_arch"] == "x86" else "64"
- webm_cpu_x86 = (
- not is_x11_or_server_arm
- and (cpu_bits == "32" or cpu_bits == "64")
- and (
- env["platform"] == "windows"
- or env["platform"] == "linuxbsd"
- or env["platform"] == "haiku"
- or is_macos_x86
- or is_android_x86
- or is_ios_x86
- )
- )
- webm_cpu_arm = (
- is_x11_or_server_arm
- or (not is_macos_x86 and env["platform"] == "osx")
- or (not is_ios_x86 and env["platform"] == "iphone")
- or (not is_android_x86 and env["platform"] == "android")
- )
-
-if webm_cpu_x86:
- import subprocess
- import os
-
- yasm_paths = [
- "yasm",
- "../../../yasm",
- ]
-
- yasm_found = False
-
- devnull = open(os.devnull)
- for yasm_path in yasm_paths:
- try:
- yasm_found = True
- subprocess.Popen([yasm_path, "--version"], stdout=devnull, stderr=devnull).communicate()
- except Exception:
- yasm_found = False
- if yasm_found:
- break
-
- if not yasm_found:
- webm_cpu_x86 = False
- print("YASM is necessary for WebM SIMD optimizations.")
-
-webm_simd_optimizations = False
-
-if webm_cpu_x86:
- if env["platform"] == "windows" or env["platform"] == "uwp":
- env_libvpx["ASFORMAT"] = "win"
- elif env["platform"] == "osx" or env["platform"] == "iphone":
- env_libvpx["ASFORMAT"] = "macho"
- else:
- env_libvpx["ASFORMAT"] = "elf"
- env_libvpx["ASFORMAT"] += cpu_bits
-
- env_libvpx["AS"] = "yasm"
- env_libvpx["ASFLAGS"] = "-I" + libvpx_dir[1:] + " -f $ASFORMAT -D $ASCPU"
- env_libvpx["ASCOM"] = "$AS $ASFLAGS -o $TARGET $SOURCES"
-
- if cpu_bits == "32":
- env_libvpx["ASCPU"] = "X86_32"
- elif cpu_bits == "64":
- env_libvpx["ASCPU"] = "X86_64"
-
- env_libvpx.Append(CPPDEFINES=["WEBM_X86ASM"])
-
- webm_simd_optimizations = True
-
-if webm_cpu_arm:
- if env["platform"] == "iphone":
- env_libvpx["ASFLAGS"] = "-arch armv7"
- elif env["platform"] == "android" and env["android_arch"] == "armv7" or env["platform"] == "linuxbsd":
- env_libvpx["ASFLAGS"] = "-mfpu=neon"
- elif env["platform"] == "uwp":
- env_libvpx["AS"] = "armasm"
- env_libvpx["ASFLAGS"] = ""
- env_libvpx["ASCOM"] = "$AS $ASFLAGS -o $TARGET $SOURCES"
-
- env_libvpx.Append(CPPDEFINES=["WEBM_ARMASM"])
-
- webm_simd_optimizations = True
-
-if webm_simd_optimizations == False:
- print("WebM SIMD optimizations are disabled. Check if your CPU architecture, CPU bits or platform are supported!")
-
-env_libvpx.add_source_files(env.modules_sources, libvpx_sources)
-
-if webm_multithread:
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_mt)
-
-if webm_cpu_x86:
- is_clang_or_gcc = (
- ("gcc" in os.path.basename(env["CC"])) or ("clang" in os.path.basename(env["CC"])) or ("osxcross" in env)
- )
-
- env_libvpx_mmx = env_libvpx.Clone()
- if cpu_bits == "32" and is_clang_or_gcc:
- env_libvpx_mmx.Append(CCFLAGS=["-mmmx"])
- env_libvpx_mmx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_mmx)
-
- env_libvpx_sse2 = env_libvpx.Clone()
- if cpu_bits == "32" and is_clang_or_gcc:
- env_libvpx_sse2.Append(CCFLAGS=["-msse2"])
- env_libvpx_sse2.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_sse2)
-
- env_libvpx_ssse3 = env_libvpx.Clone()
- if is_clang_or_gcc:
- env_libvpx_ssse3.Append(CCFLAGS=["-mssse3"])
- env_libvpx_ssse3.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_ssse3)
-
- env_libvpx_avx2 = env_libvpx.Clone()
- if is_clang_or_gcc:
- env_libvpx_avx2.Append(CCFLAGS=["-mavx2"])
- env_libvpx_avx2.add_source_files(env.modules_sources, libvpx_sources_intrin_x86_avx2)
-
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_intrin_x86)
-
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86asm)
- if cpu_bits == "64":
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_x86_64asm)
-elif webm_cpu_arm:
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm)
- if env["platform"] == "android":
- env_libvpx.Prepend(CPPPATH=[libvpx_dir + "third_party/android"])
- env_libvpx.add_source_files(env.modules_sources, [libvpx_dir + "third_party/android/cpu-features.c"])
-
- env_libvpx_neon = env_libvpx.Clone()
- env_libvpx_neon.add_source_files(env.modules_sources, libvpx_sources_arm_neon)
-
- if env["platform"] == "uwp":
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_armasm_ms)
- elif env["platform"] == "iphone":
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas_apple)
- elif (is_x11_or_server_arm and cpu_bits == "32") or (
- env["platform"] == "android" and not env["android_arch"] == "arm64v8"
- ):
- env_libvpx.add_source_files(env.modules_sources, libvpx_sources_arm_neon_gas)
diff --git a/modules/webm/register_types.h b/modules/webm/register_types.h
deleted file mode 100644
index d090fe745b..0000000000
--- a/modules/webm/register_types.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*************************************************************************/
-/* register_types.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef WEBM_REGISTER_TYPES_H
-#define WEBM_REGISTER_TYPES_H
-
-void register_webm_types();
-void unregister_webm_types();
-
-#endif // WEBM_REGISTER_TYPES_H
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
deleted file mode 100644
index 12e0f5bd25..0000000000
--- a/modules/webm/video_stream_webm.cpp
+++ /dev/null
@@ -1,469 +0,0 @@
-/*************************************************************************/
-/* video_stream_webm.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "video_stream_webm.h"
-
-#include "core/config/project_settings.h"
-#include "core/io/file_access.h"
-#include "core/os/os.h"
-#include "servers/audio_server.h"
-
-#include "thirdparty/misc/yuv2rgb.h"
-
-// libsimplewebm
-#include <OpusVorbisDecoder.hpp>
-#include <VPXDecoder.hpp>
-
-// libvpx
-#include <vpx/vpx_image.h>
-
-// libwebm
-#include <mkvparser/mkvparser.h>
-
-class MkvReader : public mkvparser::IMkvReader {
-public:
- MkvReader(const String &p_file) {
- file = FileAccess::open(p_file, FileAccess::READ);
-
- ERR_FAIL_COND_MSG(!file, "Failed loading resource: '" + p_file + "'.");
- }
- ~MkvReader() {
- if (file) {
- memdelete(file);
- }
- }
-
- virtual int Read(long long pos, long len, unsigned char *buf) {
- if (file) {
- if (file->get_position() != (uint64_t)pos) {
- file->seek(pos);
- }
- if (file->get_buffer(buf, len) == (uint64_t)len) {
- return 0;
- }
- }
- return -1;
- }
-
- virtual int Length(long long *total, long long *available) {
- if (file) {
- const uint64_t len = file->get_length();
- if (total) {
- *total = len;
- }
- if (available) {
- *available = len;
- }
- return 0;
- }
- return -1;
- }
-
-private:
- FileAccess *file;
-};
-
-/**/
-
-VideoStreamPlaybackWebm::VideoStreamPlaybackWebm() :
-
- texture(memnew(ImageTexture)) {}
-VideoStreamPlaybackWebm::~VideoStreamPlaybackWebm() {
- delete_pointers();
-}
-
-bool VideoStreamPlaybackWebm::open_file(const String &p_file) {
- file_name = p_file;
- webm = memnew(WebMDemuxer(new MkvReader(file_name), 0, audio_track));
- if (webm->isOpen()) {
- video = memnew(VPXDecoder(*webm, OS::get_singleton()->get_processor_count()));
- if (video->isOpen()) {
- audio = memnew(OpusVorbisDecoder(*webm));
- if (audio->isOpen()) {
- audio_frame = memnew(WebMFrame);
- pcm = (float *)memalloc(sizeof(float) * audio->getBufferSamples() * webm->getChannels());
- } else {
- memdelete(audio);
- audio = nullptr;
- }
-
- frame_data.resize((webm->getWidth() * webm->getHeight()) << 2);
- Ref<Image> img;
- img.instantiate();
- img->create(webm->getWidth(), webm->getHeight(), false, Image::FORMAT_RGBA8);
- texture->create_from_image(img);
-
- return true;
- }
- memdelete(video);
- video = nullptr;
- }
- memdelete(webm);
- webm = nullptr;
- return false;
-}
-
-void VideoStreamPlaybackWebm::stop() {
- if (playing) {
- delete_pointers();
-
- pcm = nullptr;
-
- audio_frame = nullptr;
- video_frames = nullptr;
-
- video = nullptr;
- audio = nullptr;
-
- open_file(file_name); //Should not fail here...
-
- video_frames_capacity = video_frames_pos = 0;
- num_decoded_samples = 0;
- samples_offset = -1;
- video_frame_delay = video_pos = 0.0;
- }
- time = 0.0;
- playing = false;
-}
-
-void VideoStreamPlaybackWebm::play() {
- stop();
-
- delay_compensation = ProjectSettings::get_singleton()->get("audio/video/video_delay_compensation_ms");
- delay_compensation /= 1000.0;
-
- playing = true;
-}
-
-bool VideoStreamPlaybackWebm::is_playing() const {
- return playing;
-}
-
-void VideoStreamPlaybackWebm::set_paused(bool p_paused) {
- paused = p_paused;
-}
-
-bool VideoStreamPlaybackWebm::is_paused() const {
- return paused;
-}
-
-void VideoStreamPlaybackWebm::set_loop(bool p_enable) {
- //Empty
-}
-
-bool VideoStreamPlaybackWebm::has_loop() const {
- return false;
-}
-
-float VideoStreamPlaybackWebm::get_length() const {
- if (webm) {
- return webm->getLength();
- }
- return 0.0f;
-}
-
-float VideoStreamPlaybackWebm::get_playback_position() const {
- return video_pos;
-}
-
-void VideoStreamPlaybackWebm::seek(float p_time) {
- WARN_PRINT_ONCE("Seeking in Theora and WebM videos is not implemented yet (it's only supported for GDNative-provided video streams).");
-}
-
-void VideoStreamPlaybackWebm::set_audio_track(int p_idx) {
- audio_track = p_idx;
-}
-
-Ref<Texture2D> VideoStreamPlaybackWebm::get_texture() const {
- return texture;
-}
-
-void VideoStreamPlaybackWebm::update(float p_delta) {
- if ((!playing || paused) || !video) {
- return;
- }
-
- time += p_delta;
-
- if (time < video_pos) {
- return;
- }
-
- bool audio_buffer_full = false;
-
- if (samples_offset > -1) {
- //Mix remaining samples
- const int to_read = num_decoded_samples - samples_offset;
- const int mixed = mix_callback(mix_udata, pcm + samples_offset * webm->getChannels(), to_read);
- if (mixed != to_read) {
- samples_offset += mixed;
- audio_buffer_full = true;
- } else {
- samples_offset = -1;
- }
- }
-
- const bool hasAudio = (audio && mix_callback);
- while ((hasAudio && !audio_buffer_full && !has_enough_video_frames()) ||
- (!hasAudio && video_frames_pos == 0)) {
- if (hasAudio && !audio_buffer_full && audio_frame->isValid() &&
- audio->getPCMF(*audio_frame, pcm, num_decoded_samples) && num_decoded_samples > 0) {
- const int mixed = mix_callback(mix_udata, pcm, num_decoded_samples);
-
- if (mixed != num_decoded_samples) {
- samples_offset = mixed;
- audio_buffer_full = true;
- }
- }
-
- WebMFrame *video_frame;
- if (video_frames_pos >= video_frames_capacity) {
- WebMFrame **video_frames_new = (WebMFrame **)memrealloc(video_frames, ++video_frames_capacity * sizeof(void *));
- ERR_FAIL_COND(!video_frames_new); //Out of memory
- (video_frames = video_frames_new)[video_frames_capacity - 1] = memnew(WebMFrame);
- }
- video_frame = video_frames[video_frames_pos];
-
- if (!webm->readFrame(video_frame, audio_frame)) { //This will invalidate frames
- break; //Can't demux, EOS?
- }
-
- if (video_frame->isValid()) {
- ++video_frames_pos;
- }
- };
-
- bool video_frame_done = false;
- while (video_frames_pos > 0 && !video_frame_done) {
- WebMFrame *video_frame = video_frames[0];
-
- // It seems VPXDecoder::decode has to be executed even though we might skip this frame
- if (video->decode(*video_frame)) {
- VPXDecoder::IMAGE_ERROR err;
- VPXDecoder::Image image;
-
- if (should_process(*video_frame)) {
- if ((err = video->getImage(image)) != VPXDecoder::NO_FRAME) {
- if (err == VPXDecoder::NO_ERROR && image.w == webm->getWidth() && image.h == webm->getHeight()) {
- uint8_t *w = frame_data.ptrw();
- bool converted = false;
-
- if (image.chromaShiftW == 0 && image.chromaShiftH == 0 && image.cs == VPX_CS_SRGB) {
- uint8_t *wp = w;
- unsigned char *rRow = image.planes[2];
- unsigned char *gRow = image.planes[0];
- unsigned char *bRow = image.planes[1];
- for (int i = 0; i < image.h; i++) {
- for (int j = 0; j < image.w; j++) {
- *wp++ = rRow[j];
- *wp++ = gRow[j];
- *wp++ = bRow[j];
- *wp++ = 255;
- }
- rRow += image.linesize[2];
- gRow += image.linesize[0];
- bRow += image.linesize[1];
- }
- converted = true;
- } else if (image.chromaShiftW == 1 && image.chromaShiftH == 1) {
- yuv420_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2);
- //libyuv::I420ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h);
- converted = true;
- } else if (image.chromaShiftW == 1 && image.chromaShiftH == 0) {
- yuv422_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2);
- //libyuv::I422ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h);
- converted = true;
- } else if (image.chromaShiftW == 0 && image.chromaShiftH == 0) {
- yuv444_2_rgb8888(w, image.planes[0], image.planes[1], image.planes[2], image.w, image.h, image.linesize[0], image.linesize[1], image.w << 2);
- //libyuv::I444ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2], image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h);
- converted = true;
- } else if (image.chromaShiftW == 2 && image.chromaShiftH == 0) {
- //libyuv::I411ToARGB(image.planes[0], image.linesize[0], image.planes[2], image.linesize[2] image.planes[1], image.linesize[1], w.ptr(), image.w << 2, image.w, image.h);
- //converted = true;
- }
-
- if (converted) {
- Ref<Image> img = memnew(Image(image.w, image.h, 0, Image::FORMAT_RGBA8, frame_data));
- texture->update(img); //Zero copy send to visual server
- video_frame_done = true;
- }
- }
- }
- }
- }
-
- video_pos = video_frame->time;
- memmove(video_frames, video_frames + 1, (--video_frames_pos) * sizeof(void *));
- video_frames[video_frames_pos] = video_frame;
- }
-
- if (video_frames_pos == 0 && webm->isEOS()) {
- stop();
- }
-}
-
-void VideoStreamPlaybackWebm::set_mix_callback(VideoStreamPlayback::AudioMixCallback p_callback, void *p_userdata) {
- mix_callback = p_callback;
- mix_udata = p_userdata;
-}
-
-int VideoStreamPlaybackWebm::get_channels() const {
- if (audio) {
- return webm->getChannels();
- }
- return 0;
-}
-
-int VideoStreamPlaybackWebm::get_mix_rate() const {
- if (audio) {
- return webm->getSampleRate();
- }
- return 0;
-}
-
-inline bool VideoStreamPlaybackWebm::has_enough_video_frames() const {
- if (video_frames_pos > 0) {
- // FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to
- // systematically return 0. Now that it gives a proper latency, it broke this
- // code where the delay compensation likely never really worked.
- //const double audio_delay = AudioServer::get_singleton()->get_output_latency();
- const double video_time = video_frames[video_frames_pos - 1]->time;
- return video_time >= time + /* audio_delay + */ delay_compensation;
- }
- return false;
-}
-
-bool VideoStreamPlaybackWebm::should_process(WebMFrame &video_frame) {
- // FIXME: AudioServer output latency was fixed in af9bb0e, previously it used to
- // systematically return 0. Now that it gives a proper latency, it broke this
- // code where the delay compensation likely never really worked.
- //const double audio_delay = AudioServer::get_singleton()->get_output_latency();
- return video_frame.time >= time + /* audio_delay + */ delay_compensation;
-}
-
-void VideoStreamPlaybackWebm::delete_pointers() {
- if (pcm) {
- memfree(pcm);
- }
-
- if (audio_frame) {
- memdelete(audio_frame);
- }
- if (video_frames) {
- for (int i = 0; i < video_frames_capacity; ++i) {
- memdelete(video_frames[i]);
- }
- memfree(video_frames);
- }
-
- if (video) {
- memdelete(video);
- }
- if (audio) {
- memdelete(audio);
- }
-
- if (webm) {
- memdelete(webm);
- }
-}
-
-/**/
-
-VideoStreamWebm::VideoStreamWebm() {}
-
-Ref<VideoStreamPlayback> VideoStreamWebm::instance_playback() {
- Ref<VideoStreamPlaybackWebm> pb = memnew(VideoStreamPlaybackWebm);
- pb->set_audio_track(audio_track);
- if (pb->open_file(file)) {
- return pb;
- }
- return nullptr;
-}
-
-void VideoStreamWebm::set_file(const String &p_file) {
- file = p_file;
-}
-
-String VideoStreamWebm::get_file() {
- return file;
-}
-
-void VideoStreamWebm::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_file", "file"), &VideoStreamWebm::set_file);
- ClassDB::bind_method(D_METHOD("get_file"), &VideoStreamWebm::get_file);
-
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "file", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_file", "get_file");
-}
-
-void VideoStreamWebm::set_audio_track(int p_track) {
- audio_track = p_track;
-}
-
-////////////
-
-RES ResourceFormatLoaderWebm::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
- if (!f) {
- if (r_error) {
- *r_error = ERR_CANT_OPEN;
- }
- return RES();
- }
-
- VideoStreamWebm *stream = memnew(VideoStreamWebm);
- stream->set_file(p_path);
-
- Ref<VideoStreamWebm> webm_stream = Ref<VideoStreamWebm>(stream);
-
- if (r_error) {
- *r_error = OK;
- }
-
- f->close();
- memdelete(f);
- return webm_stream;
-}
-
-void ResourceFormatLoaderWebm::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("webm");
-}
-
-bool ResourceFormatLoaderWebm::handles_type(const String &p_type) const {
- return ClassDB::is_parent_class(p_type, "VideoStream");
-}
-
-String ResourceFormatLoaderWebm::get_resource_type(const String &p_path) const {
- String el = p_path.get_extension().to_lower();
- if (el == "webm") {
- return "VideoStreamWebm";
- }
- return "";
-}
diff --git a/modules/webm/video_stream_webm.h b/modules/webm/video_stream_webm.h
deleted file mode 100644
index 60e02ab38b..0000000000
--- a/modules/webm/video_stream_webm.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*************************************************************************/
-/* video_stream_webm.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef VIDEO_STREAM_WEBM_H
-#define VIDEO_STREAM_WEBM_H
-
-#include "core/io/resource_loader.h"
-#include "scene/resources/video_stream.h"
-
-class WebMFrame;
-class WebMDemuxer;
-class VPXDecoder;
-class OpusVorbisDecoder;
-
-class VideoStreamPlaybackWebm : public VideoStreamPlayback {
- GDCLASS(VideoStreamPlaybackWebm, VideoStreamPlayback);
-
- String file_name;
- int audio_track = 0;
-
- WebMDemuxer *webm = nullptr;
- VPXDecoder *video = nullptr;
- OpusVorbisDecoder *audio = nullptr;
-
- WebMFrame **video_frames = nullptr, *audio_frame = nullptr;
- int video_frames_pos = 0, video_frames_capacity = 0;
-
- int num_decoded_samples = 0, samples_offset = -1;
- AudioMixCallback mix_callback = nullptr;
- void *mix_udata = nullptr;
-
- bool playing = false, paused = false;
- double delay_compensation = 0.0;
- double time = 0.0, video_frame_delay = 0.0, video_pos = 0.0;
-
- Vector<uint8_t> frame_data;
- Ref<ImageTexture> texture;
-
- float *pcm = nullptr;
-
-public:
- VideoStreamPlaybackWebm();
- ~VideoStreamPlaybackWebm();
-
- bool open_file(const String &p_file);
-
- virtual void stop() override;
- virtual void play() override;
-
- virtual bool is_playing() const override;
-
- virtual void set_paused(bool p_paused) override;
- virtual bool is_paused() const override;
-
- virtual void set_loop(bool p_enable) override;
- virtual bool has_loop() const override;
-
- virtual float get_length() const override;
-
- virtual float get_playback_position() const override;
- virtual void seek(float p_time) override;
-
- virtual void set_audio_track(int p_idx) override;
-
- virtual Ref<Texture2D> get_texture() const override;
- virtual void update(float p_delta) override;
-
- virtual void set_mix_callback(AudioMixCallback p_callback, void *p_userdata) override;
- virtual int get_channels() const override;
- virtual int get_mix_rate() const override;
-
-private:
- inline bool has_enough_video_frames() const;
- bool should_process(WebMFrame &video_frame);
-
- void delete_pointers();
-};
-
-/**/
-
-class VideoStreamWebm : public VideoStream {
- GDCLASS(VideoStreamWebm, VideoStream);
-
- String file;
- int audio_track = 0;
-
-protected:
- static void _bind_methods();
-
-public:
- VideoStreamWebm();
-
- virtual Ref<VideoStreamPlayback> instance_playback() override;
-
- virtual void set_file(const String &p_file);
- String get_file();
- virtual void set_audio_track(int p_track) override;
-};
-
-class ResourceFormatLoaderWebm : public ResourceFormatLoader {
-public:
- virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
- virtual void get_recognized_extensions(List<String> *p_extensions) const;
- virtual bool handles_type(const String &p_type) const;
- virtual String get_resource_type(const String &p_path) const;
-};
-
-#endif // VIDEO_STREAM_WEBM_H
diff --git a/modules/webp/SCsub b/modules/webp/SCsub
index 4c0c2f7893..80d62400c8 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -67,6 +67,7 @@ if env["builtin_libwebp"]:
"dsp/lossless_msa.c",
"dsp/lossless_neon.c",
"dsp/lossless_sse2.c",
+ "dsp/lossless_sse41.c",
"dsp/rescaler.c",
"dsp/rescaler_mips32.c",
"dsp/rescaler_mips_dsp_r2.c",
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index 31b8a73bf2..e6b9959840 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -4,11 +4,6 @@ Import("env")
Import("env_modules")
env_webrtc = env_modules.Clone()
-use_gdnative = env_webrtc["module_gdnative_enabled"]
-
-if use_gdnative: # GDNative is retained in Javascript for export compatibility
- env_webrtc.Append(CPPDEFINES=["WEBRTC_GDNATIVE_ENABLED"])
- env_webrtc.Prepend(CPPPATH=["#modules/gdnative/include/"])
if env["platform"] == "javascript":
# Our JavaScript/C++ interface.
diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py
index 3281415f38..4ad918833a 100644
--- a/modules/webrtc/config.py
+++ b/modules/webrtc/config.py
@@ -11,6 +11,8 @@ def get_doc_classes():
"WebRTCPeerConnection",
"WebRTCDataChannel",
"WebRTCMultiplayerPeer",
+ "WebRTCPeerConnectionExtension",
+ "WebRTCDataChannelExtension",
]
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml
new file mode 100644
index 0000000000..746fabd6e5
--- /dev/null
+++ b/modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCDataChannelExtension" inherits="WebRTCDataChannel" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_close" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_available_packet_count" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_buffered_amount" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_id" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_label" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_max_packet_life_time" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_max_packet_size" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_max_retransmits" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_packet" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="r_buffer" type="const uint8_t **" />
+ <argument index="1" name="r_buffer_size" type="int32_t*" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_protocol" qualifiers="virtual const">
+ <return type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_ready_state" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_write_mode" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_is_negotiated" qualifiers="virtual const">
+ <return type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="_is_ordered" qualifiers="virtual const">
+ <return type="bool" />
+ <description>
+ </description>
+ </method>
+ <method name="_poll" qualifiers="virtual">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_put_packet" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="p_buffer" type="const uint8_t*" />
+ <argument index="1" name="p_buffer_size" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_set_write_mode" qualifiers="virtual">
+ <return type="void" />
+ <argument index="0" name="p_write_mode" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_was_string_packet" qualifiers="virtual const">
+ <return type="bool" />
+ <description>
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index c53af22ae1..a8360a4d45 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -4,9 +4,10 @@
A simple interface to create a peer-to-peer mesh network composed of [WebRTCPeerConnection] that is compatible with the [MultiplayerAPI].
</brief_description>
<description>
- This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.network_peer].
+ This class constructs a full mesh of [WebRTCPeerConnection] (one connection for each peer) that can be used as a [member MultiplayerAPI.multiplayer_peer].
You can add each [WebRTCPeerConnection] via [method add_peer] or remove them via [method remove_peer]. Peers must be added in [constant WebRTCPeerConnection.STATE_NEW] state to allow it to create the appropriate channels. This class will not create offers nor set descriptions, it will only poll them, and notify connections and disconnections.
[signal MultiplayerPeer.connection_succeeded] and [signal MultiplayerPeer.server_disconnected] will not be emitted unless [code]server_compatibility[/code] is [code]true[/code] in [method initialize]. Beside that data transfer works like in a [MultiplayerPeer].
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
</tutorials>
@@ -56,7 +57,7 @@
Initialize the multiplayer peer with the given [code]peer_id[/code] (must be between 1 and 2147483647).
If [code]server_compatibilty[/code] is [code]false[/code] (default), the multiplayer peer will be immediately in state [constant MultiplayerPeer.CONNECTION_CONNECTED] and [signal MultiplayerPeer.connection_succeeded] will not be emitted.
If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal MultiplayerPeer.peer_connected] signals until a peer with id [constant MultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal MultiplayerPeer.connection_succeeded]. After that the signal [signal MultiplayerPeer.peer_connected] will be emitted for every already connected peer, and any new peer that might connect. If the server peer disconnects after that, signal [signal MultiplayerPeer.server_disconnected] will be emitted and state will become [constant MultiplayerPeer.CONNECTION_CONNECTED].
- You can optionally specify a [code]channels_config[/code] array of [enum MultiplayerPeer.TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
+ You can optionally specify a [code]channels_config[/code] array of [enum TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="remove_peer">
@@ -67,10 +68,4 @@
</description>
</method>
</methods>
- <members>
- <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
- <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
- </members>
- <constants>
- </constants>
</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index f6f360503f..618fe14137 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -26,7 +26,8 @@
<method name="close">
<return type="void" />
<description>
- Close the peer connection and all data channels associated with it. Note, you cannot reuse this object for a new connection unless you call [method initialize].
+ Close the peer connection and all data channels associated with it.
+ [b]Note:[/b] You cannot reuse this object for a new connection unless you call [method initialize].
</description>
</method>
<method name="create_data_channel">
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
new file mode 100644
index 0000000000..d296fcd6e7
--- /dev/null
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebRTCPeerConnectionExtension" inherits="WebRTCPeerConnection" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_add_ice_candidate" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="p_sdp_mid_name" type="String" />
+ <argument index="1" name="p_sdp_mline_index" type="int" />
+ <argument index="2" name="p_sdp_name" type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="_close" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ </description>
+ </method>
+ <method name="_create_data_channel" qualifiers="virtual">
+ <return type="Object" />
+ <argument index="0" name="p_label" type="String" />
+ <argument index="1" name="p_config" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="_create_offer" qualifiers="virtual">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_get_connection_state" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_initialize" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="p_config" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="_poll" qualifiers="virtual">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="_set_local_description" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="p_type" type="String" />
+ <argument index="1" name="p_sdp" type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="_set_remote_description" qualifiers="virtual">
+ <return type="int" />
+ <argument index="0" name="p_type" type="String" />
+ <argument index="1" name="p_sdp" type="String" />
+ <description>
+ </description>
+ </method>
+ <method name="make_default">
+ <return type="void" />
+ <description>
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 63ecc03a4c..8110e4a048 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -31,17 +31,11 @@
#include "register_types.h"
#include "core/config/project_settings.h"
#include "webrtc_data_channel.h"
+#include "webrtc_multiplayer_peer.h"
#include "webrtc_peer_connection.h"
-#ifdef JAVASCRIPT_ENABLED
-#include "emscripten.h"
-#include "webrtc_peer_connection_js.h"
-#endif
-#ifdef WEBRTC_GDNATIVE_ENABLED
-#include "webrtc_data_channel_gdnative.h"
-#include "webrtc_peer_connection_gdnative.h"
-#endif
-#include "webrtc_multiplayer_peer.h"
+#include "webrtc_data_channel_extension.h"
+#include "webrtc_peer_connection_extension.h"
void register_webrtc_types() {
#define _SET_HINT(NAME, _VAL_, _MAX_) \
@@ -50,18 +44,12 @@ void register_webrtc_types() {
_SET_HINT(WRTC_IN_BUF, 64, 4096);
-#ifdef JAVASCRIPT_ENABLED
- WebRTCPeerConnectionJS::make_default();
-#elif defined(WEBRTC_GDNATIVE_ENABLED)
- WebRTCPeerConnectionGDNative::make_default();
-#endif
-
ClassDB::register_custom_instance_class<WebRTCPeerConnection>();
-#ifdef WEBRTC_GDNATIVE_ENABLED
- GDREGISTER_CLASS(WebRTCPeerConnectionGDNative);
- GDREGISTER_CLASS(WebRTCDataChannelGDNative);
-#endif
+ GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
+
GDREGISTER_VIRTUAL_CLASS(WebRTCDataChannel);
+ GDREGISTER_CLASS(WebRTCDataChannelExtension);
+
GDREGISTER_CLASS(WebRTCMultiplayerPeer);
}
diff --git a/modules/webrtc/webrtc_data_channel_extension.cpp b/modules/webrtc/webrtc_data_channel_extension.cpp
new file mode 100644
index 0000000000..ae346f6d8e
--- /dev/null
+++ b/modules/webrtc/webrtc_data_channel_extension.cpp
@@ -0,0 +1,215 @@
+/*************************************************************************/
+/* webrtc_data_channel_extension.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "webrtc_data_channel_extension.h"
+
+void WebRTCDataChannelExtension::_bind_methods() {
+ ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY);
+
+ GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
+ GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
+ GDVIRTUAL_BIND(_get_available_packet_count);
+ GDVIRTUAL_BIND(_get_max_packet_size);
+
+ GDVIRTUAL_BIND(_poll);
+ GDVIRTUAL_BIND(_close);
+
+ GDVIRTUAL_BIND(_set_write_mode, "p_write_mode");
+ GDVIRTUAL_BIND(_get_write_mode);
+
+ GDVIRTUAL_BIND(_was_string_packet);
+ GDVIRTUAL_BIND(_get_ready_state);
+ GDVIRTUAL_BIND(_get_label);
+ GDVIRTUAL_BIND(_is_ordered);
+ GDVIRTUAL_BIND(_get_id);
+ GDVIRTUAL_BIND(_get_max_packet_life_time);
+ GDVIRTUAL_BIND(_get_max_retransmits);
+ GDVIRTUAL_BIND(_get_protocol);
+ GDVIRTUAL_BIND(_is_negotiated);
+ GDVIRTUAL_BIND(_get_buffered_amount);
+}
+
+int WebRTCDataChannelExtension::get_available_packet_count() const {
+ int count;
+ if (GDVIRTUAL_CALL(_get_available_packet_count, count)) {
+ return count;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_available_packet_count is unimplemented!");
+ return -1;
+}
+
+Error WebRTCDataChannelExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_packet_native is unimplemented!");
+ return FAILED;
+}
+
+Error WebRTCDataChannelExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ int err;
+ if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_put_packet_native is unimplemented!");
+ return FAILED;
+}
+
+int WebRTCDataChannelExtension::get_max_packet_size() const {
+ int size;
+ if (GDVIRTUAL_CALL(_get_max_packet_size, size)) {
+ return size;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_size is unimplemented!");
+ return 0;
+}
+
+Error WebRTCDataChannelExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_poll is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+void WebRTCDataChannelExtension::close() {
+ if (GDVIRTUAL_CALL(_close)) {
+ return;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_close is unimplemented!");
+}
+
+void WebRTCDataChannelExtension::set_write_mode(WriteMode p_mode) {
+ if (GDVIRTUAL_CALL(_set_write_mode, p_mode)) {
+ return;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_set_write_mode is unimplemented!");
+}
+
+WebRTCDataChannel::WriteMode WebRTCDataChannelExtension::get_write_mode() const {
+ int mode;
+ if (GDVIRTUAL_CALL(_get_write_mode, mode)) {
+ return (WriteMode)mode;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_write_mode is unimplemented!");
+ return WRITE_MODE_BINARY;
+}
+
+bool WebRTCDataChannelExtension::was_string_packet() const {
+ bool was_string;
+ if (GDVIRTUAL_CALL(_was_string_packet, was_string)) {
+ return was_string;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_was_string_packet is unimplemented!");
+ return false;
+}
+
+WebRTCDataChannel::ChannelState WebRTCDataChannelExtension::get_ready_state() const {
+ int state;
+ if (GDVIRTUAL_CALL(_get_ready_state, state)) {
+ return (ChannelState)state;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_ready_state is unimplemented!");
+ return STATE_CLOSED;
+}
+
+String WebRTCDataChannelExtension::get_label() const {
+ String label;
+ if (GDVIRTUAL_CALL(_get_label, label)) {
+ return label;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_label is unimplemented!");
+ return label;
+}
+
+bool WebRTCDataChannelExtension::is_ordered() const {
+ bool ordered;
+ if (GDVIRTUAL_CALL(_is_ordered, ordered)) {
+ return ordered;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_ordered is unimplemented!");
+ return false;
+}
+
+int WebRTCDataChannelExtension::get_id() const {
+ int id;
+ if (GDVIRTUAL_CALL(_get_id, id)) {
+ return id;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_id is unimplemented!");
+ return -1;
+}
+
+int WebRTCDataChannelExtension::get_max_packet_life_time() const {
+ int lifetime;
+ if (GDVIRTUAL_CALL(_get_max_packet_life_time, lifetime)) {
+ return lifetime;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_packet_life_time is unimplemented!");
+ return -1;
+}
+
+int WebRTCDataChannelExtension::get_max_retransmits() const {
+ int retransmits;
+ if (GDVIRTUAL_CALL(_get_max_retransmits, retransmits)) {
+ return retransmits;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_max_retransmits is unimplemented!");
+ return -1;
+}
+
+String WebRTCDataChannelExtension::get_protocol() const {
+ String protocol;
+ if (GDVIRTUAL_CALL(_get_protocol, protocol)) {
+ return protocol;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_protocol is unimplemented!");
+ return protocol;
+}
+
+bool WebRTCDataChannelExtension::is_negotiated() const {
+ bool negotiated;
+ if (GDVIRTUAL_CALL(_is_negotiated, negotiated)) {
+ return negotiated;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_is_negotiated is unimplemented!");
+ return false;
+}
+
+int WebRTCDataChannelExtension::get_buffered_amount() const {
+ int amount;
+ if (GDVIRTUAL_CALL(_get_buffered_amount, amount)) {
+ return amount;
+ }
+ WARN_PRINT_ONCE("WebRTCDataChannelExtension::_get_buffered_amount is unimplemented!");
+ return -1;
+}
diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_extension.h
index 5c80edd48c..eec96b4c62 100644
--- a/modules/webrtc/webrtc_data_channel_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_extension.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* webrtc_data_channel_gdnative.h */
+/* webrtc_data_channel_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,26 +28,22 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_DATA_CHANNEL_GDNATIVE_H
-#define WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#ifndef WEBRTC_DATA_CHANNEL_EXTENSION_H
+#define WEBRTC_DATA_CHANNEL_EXTENSION_H
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
-#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_data_channel.h"
-class WebRTCDataChannelGDNative : public WebRTCDataChannel {
- GDCLASS(WebRTCDataChannelGDNative, WebRTCDataChannel);
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
+class WebRTCDataChannelExtension : public WebRTCDataChannel {
+ GDCLASS(WebRTCDataChannelExtension, WebRTCDataChannel);
protected:
static void _bind_methods();
-private:
- const godot_net_webrtc_data_channel *interface;
-
public:
- void set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl);
-
virtual void set_write_mode(WriteMode mode) override;
virtual WriteMode get_write_mode() const override;
virtual bool was_string_packet() const override;
@@ -72,10 +68,31 @@ public:
virtual int get_max_packet_size() const override;
- WebRTCDataChannelGDNative();
- ~WebRTCDataChannelGDNative();
-};
+ /** GDExtension **/
+ GDVIRTUAL0RC(int, _get_available_packet_count);
+ GDVIRTUAL2R(int, _get_packet, GDNativeConstPtr<const uint8_t *>, GDNativePtr<int>);
+ GDVIRTUAL2R(int, _put_packet, GDNativeConstPtr<const uint8_t>, int);
+ GDVIRTUAL0RC(int, _get_max_packet_size);
-#endif // WEBRTC_GDNATIVE_ENABLED
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL0(_close);
+
+ GDVIRTUAL1(_set_write_mode, int);
+ GDVIRTUAL0RC(int, _get_write_mode);
+
+ GDVIRTUAL0RC(bool, _was_string_packet);
+
+ GDVIRTUAL0RC(int, _get_ready_state);
+ GDVIRTUAL0RC(String, _get_label);
+ GDVIRTUAL0RC(bool, _is_ordered);
+ GDVIRTUAL0RC(int, _get_id);
+ GDVIRTUAL0RC(int, _get_max_packet_life_time);
+ GDVIRTUAL0RC(int, _get_max_retransmits);
+ GDVIRTUAL0RC(String, _get_protocol);
+ GDVIRTUAL0RC(bool, _is_negotiated);
+ GDVIRTUAL0RC(int, _get_buffered_amount);
+
+ WebRTCDataChannelExtension() {}
+};
-#endif // WEBRTC_DATA_CHANNEL_GDNATIVE_H
+#endif // WEBRTC_DATA_CHANNEL_EXTENSION_H
diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp
deleted file mode 100644
index 10a3367557..0000000000
--- a/modules/webrtc/webrtc_data_channel_gdnative.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*************************************************************************/
-/* webrtc_data_channel_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
-#include "webrtc_data_channel_gdnative.h"
-
-#include "core/io/resource_loader.h"
-#include "modules/gdnative/nativescript/nativescript.h"
-
-void WebRTCDataChannelGDNative::_bind_methods() {
- ADD_PROPERTY_DEFAULT("write_mode", WRITE_MODE_BINARY);
-}
-
-WebRTCDataChannelGDNative::WebRTCDataChannelGDNative() {
- interface = nullptr;
-}
-
-WebRTCDataChannelGDNative::~WebRTCDataChannelGDNative() {
-}
-
-Error WebRTCDataChannelGDNative::poll() {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->poll(interface->data);
-}
-
-void WebRTCDataChannelGDNative::close() {
- ERR_FAIL_COND(interface == nullptr);
- interface->close(interface->data);
-}
-
-void WebRTCDataChannelGDNative::set_write_mode(WriteMode p_mode) {
- ERR_FAIL_COND(interface == nullptr);
- interface->set_write_mode(interface->data, p_mode);
-}
-
-WebRTCDataChannel::WriteMode WebRTCDataChannelGDNative::get_write_mode() const {
- ERR_FAIL_COND_V(interface == nullptr, WRITE_MODE_BINARY);
- return (WriteMode)interface->get_write_mode(interface->data);
-}
-
-bool WebRTCDataChannelGDNative::was_string_packet() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->was_string_packet(interface->data);
-}
-
-WebRTCDataChannel::ChannelState WebRTCDataChannelGDNative::get_ready_state() const {
- ERR_FAIL_COND_V(interface == nullptr, STATE_CLOSED);
- return (ChannelState)interface->get_ready_state(interface->data);
-}
-
-String WebRTCDataChannelGDNative::get_label() const {
- ERR_FAIL_COND_V(interface == nullptr, "");
- return String(interface->get_label(interface->data));
-}
-
-bool WebRTCDataChannelGDNative::is_ordered() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->is_ordered(interface->data);
-}
-
-int WebRTCDataChannelGDNative::get_id() const {
- ERR_FAIL_COND_V(interface == nullptr, -1);
- return interface->get_id(interface->data);
-}
-
-int WebRTCDataChannelGDNative::get_max_packet_life_time() const {
- ERR_FAIL_COND_V(interface == nullptr, -1);
- return interface->get_max_packet_life_time(interface->data);
-}
-
-int WebRTCDataChannelGDNative::get_max_retransmits() const {
- ERR_FAIL_COND_V(interface == nullptr, -1);
- return interface->get_max_retransmits(interface->data);
-}
-
-String WebRTCDataChannelGDNative::get_protocol() const {
- ERR_FAIL_COND_V(interface == nullptr, "");
- return String(interface->get_protocol(interface->data));
-}
-
-bool WebRTCDataChannelGDNative::is_negotiated() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
- return interface->is_negotiated(interface->data);
-}
-
-int WebRTCDataChannelGDNative::get_buffered_amount() const {
- ERR_FAIL_COND_V(interface == NULL, 0);
- return interface->get_buffered_amount(interface->data);
-}
-
-Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size);
-}
-
-Error WebRTCDataChannelGDNative::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->put_packet(interface->data, p_buffer, p_buffer_size);
-}
-
-int WebRTCDataChannelGDNative::get_max_packet_size() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_max_packet_size(interface->data);
-}
-
-int WebRTCDataChannelGDNative::get_available_packet_count() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_packet_count(interface->data);
-}
-
-void WebRTCDataChannelGDNative::set_native_webrtc_data_channel(const godot_net_webrtc_data_channel *p_impl) {
- interface = p_impl;
-}
-
-#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_multiplayer_peer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index 95c8c13449..20fe63bf7d 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -43,22 +43,6 @@ void WebRTCMultiplayerPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
}
-void WebRTCMultiplayerPeer::set_transfer_channel(int p_channel) {
- transfer_channel = p_channel;
-}
-
-int WebRTCMultiplayerPeer::get_transfer_channel() const {
- return transfer_channel;
-}
-
-void WebRTCMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
- transfer_mode = p_mode;
-}
-
-MultiplayerPeer::TransferMode WebRTCMultiplayerPeer::get_transfer_mode() const {
- return transfer_mode;
-}
-
void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) {
target_peer = p_peer_id;
}
@@ -79,8 +63,8 @@ void WebRTCMultiplayerPeer::poll() {
List<int> remove;
List<int> add;
- for (Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.front(); E; E = E->next()) {
- Ref<ConnectedPeer> peer = E->get();
+ for (KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
+ Ref<ConnectedPeer> peer = E.value;
peer->connection->poll();
// Check peer state
switch (peer->connection->get_connection_state()) {
@@ -93,7 +77,7 @@ void WebRTCMultiplayerPeer::poll() {
break;
default:
// Peer is closed or in error state. Got to next peer.
- remove.push_back(E->key());
+ remove.push_back(E.key);
continue;
}
// Check channels state
@@ -108,7 +92,7 @@ void WebRTCMultiplayerPeer::poll() {
continue;
default:
// Channel was closed or in error state, remove peer id.
- remove.push_back(E->key());
+ remove.push_back(E.key);
}
// We got a closed channel break out, the peer will be removed.
break;
@@ -116,7 +100,7 @@ void WebRTCMultiplayerPeer::poll() {
// This peer has newly connected, and all channels are now open.
if (ready == peer->channels.size() && !peer->connected) {
peer->connected = true;
- add.push_back(E->key());
+ add.push_back(E.key);
}
}
// Remove disconnected peers
@@ -141,9 +125,9 @@ void WebRTCMultiplayerPeer::poll() {
emit_signal(SNAME("peer_connected"), TARGET_PEER_SERVER);
emit_signal(SNAME("connection_succeeded"));
// Notify of all previously connected peers
- for (Map<int, Ref<ConnectedPeer>>::Element *F = peer_map.front(); F; F = F->next()) {
- if (F->key() != 1 && F->get()->connected) {
- emit_signal(SNAME("peer_connected"), F->key());
+ for (const KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
+ if (F.key != 1 && F.value->connected) {
+ emit_signal(SNAME("peer_connected"), F.key);
}
}
break; // Because we already notified of all newly added peers.
@@ -162,6 +146,10 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
}
// After last.
while (E) {
+ if (!E->get()->connected) {
+ E = E->next();
+ continue;
+ }
for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
if (F->get_available_packet_count()) {
next_packet_peer = E->key();
@@ -173,6 +161,10 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
E = peer_map.front();
// Before last
while (E) {
+ if (!E->get()->connected) {
+ E = E->next();
+ continue;
+ }
for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
if (F->get_available_packet_count()) {
next_packet_peer = E->key();
@@ -188,14 +180,6 @@ void WebRTCMultiplayerPeer::_find_next_peer() {
next_packet_peer = 0;
}
-void WebRTCMultiplayerPeer::set_refuse_new_connections(bool p_enable) {
- refuse_connections = p_enable;
-}
-
-bool WebRTCMultiplayerPeer::is_refusing_new_connections() const {
- return refuse_connections;
-}
-
MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() const {
return connection_status;
}
@@ -204,7 +188,7 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
ERR_FAIL_COND_V(p_self_id < 1 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
channels_config.clear();
for (int i = 0; i < p_channels_config.size(); i++) {
- ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'");
+ ERR_FAIL_COND_V_MSG(p_channels_config[i].get_type() != Variant::INT, ERR_INVALID_PARAMETER, "The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'");
int mode = p_channels_config[i].operator int();
// Initialize data channel configurations.
Dictionary cfg;
@@ -213,17 +197,17 @@ Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Arr
cfg["ordered"] = true;
switch (mode) {
- case TRANSFER_MODE_UNRELIABLE_ORDERED:
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
cfg["maxPacketLifetime"] = 1;
break;
- case TRANSFER_MODE_UNRELIABLE:
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE:
cfg["maxPacketLifetime"] = 1;
cfg["ordered"] = false;
break;
- case TRANSFER_MODE_RELIABLE:
+ case Multiplayer::TRANSFER_MODE_RELIABLE:
break;
default:
- ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.TransferMode'. Got: %d", mode));
+ ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("The 'channels_config' array must contain only enum values from 'MultiplayerPeer.Multiplayer::TransferMode'. Got: %d", mode));
}
channels_config.push_back(cfg);
}
@@ -268,10 +252,10 @@ Dictionary WebRTCMultiplayerPeer::get_peer(int p_peer_id) {
Dictionary WebRTCMultiplayerPeer::get_peers() {
Dictionary out;
- for (Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.front(); E; E = E->next()) {
+ for (const KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
Dictionary d;
- _peer_to_dict(E->get(), d);
- out[E->key()] = d;
+ _peer_to_dict(E.value, d);
+ out[E.key] = d;
}
return out;
}
@@ -279,7 +263,7 @@ Dictionary WebRTCMultiplayerPeer::get_peers() {
Error WebRTCMultiplayerPeer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
ERR_FAIL_COND_V(p_peer_id < 0 || p_peer_id > ~(1 << 31), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_unreliable_lifetime < 0, ERR_INVALID_PARAMETER);
- ERR_FAIL_COND_V(refuse_connections, ERR_UNAUTHORIZED);
+ ERR_FAIL_COND_V(is_refusing_new_connections(), ERR_UNAUTHORIZED);
// Peer must be valid, and in new state (to create data channels)
ERR_FAIL_COND_V(!p_peer.is_valid(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(p_peer->get_connection_state() != WebRTCPeerConnection::STATE_NEW, ERR_INVALID_PARAMETER);
@@ -352,16 +336,16 @@ Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_
Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, ERR_UNCONFIGURED);
- int ch = transfer_channel;
+ int ch = get_transfer_channel();
if (ch == 0) {
- switch (transfer_mode) {
- case TRANSFER_MODE_RELIABLE:
+ switch (get_transfer_mode()) {
+ case Multiplayer::TRANSFER_MODE_RELIABLE:
ch = CH_RELIABLE;
break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED:
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
ch = CH_ORDERED;
break;
- case TRANSFER_MODE_UNRELIABLE:
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE:
ch = CH_UNRELIABLE;
break;
}
@@ -382,15 +366,15 @@ Error WebRTCMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_si
} else {
int exclude = -target_peer;
- for (Map<int, Ref<ConnectedPeer>>::Element *F = peer_map.front(); F; F = F->next()) {
+ for (KeyValue<int, Ref<ConnectedPeer>> &F : peer_map) {
// Exclude packet. If target_peer == 0 then don't exclude any packets
- if (target_peer != 0 && F->key() == exclude) {
+ if (target_peer != 0 && F.key == exclude) {
continue;
}
- ERR_CONTINUE_MSG(F->value()->channels.size() <= ch, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
- ERR_CONTINUE(F->value()->channels[ch].is_null());
- F->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
+ ERR_CONTINUE_MSG(F.value->channels.size() <= ch, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
+ ERR_CONTINUE(F.value->channels[ch].is_null());
+ F.value->channels[ch]->put_packet(p_buffer, p_buffer_size);
}
}
return OK;
@@ -401,8 +385,11 @@ int WebRTCMultiplayerPeer::get_available_packet_count() const {
return 0; // To be sure next call to get_packet works if size > 0 .
}
int size = 0;
- for (Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.front(); E; E = E->next()) {
- for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ for (const KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
+ if (!E.value->connected) {
+ continue;
+ }
+ for (const Ref<WebRTCDataChannel> &F : E.value->channels) {
size += F->get_available_packet_count();
}
}
diff --git a/modules/webrtc/webrtc_multiplayer_peer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index ef4fe1678c..4a7e9ad7c8 100644
--- a/modules/webrtc/webrtc_multiplayer_peer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -31,7 +31,7 @@
#ifndef WEBRTC_MULTIPLAYER_H
#define WEBRTC_MULTIPLAYER_H
-#include "core/io/multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_peer.h"
#include "webrtc_peer_connection.h"
class WebRTCMultiplayerPeer : public MultiplayerPeer {
@@ -65,10 +65,7 @@ private:
uint32_t unique_id = 0;
int target_peer = 0;
int client_count = 0;
- bool refuse_connections = false;
ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
- int transfer_channel = 0;
- TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
int next_packet_peer = 0;
bool server_compat = false;
@@ -97,10 +94,6 @@ public:
int get_max_packet_size() const override;
// MultiplayerPeer
- void set_transfer_channel(int p_channel) override;
- int get_transfer_channel() const override;
- void set_transfer_mode(TransferMode p_mode) override;
- TransferMode get_transfer_mode() const override;
void set_target_peer(int p_peer_id) override;
int get_unique_id() const override;
@@ -110,9 +103,6 @@ public:
void poll() override;
- void set_refuse_new_connections(bool p_enable) override;
- bool is_refusing_new_connections() const override;
-
ConnectionStatus get_connection_status() const override;
};
diff --git a/modules/webrtc/webrtc_peer_connection.cpp b/modules/webrtc/webrtc_peer_connection.cpp
index 3e2938bf7d..ad28aa76c7 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -30,17 +30,29 @@
#include "webrtc_peer_connection.h"
-WebRTCPeerConnection *(*WebRTCPeerConnection::_create)() = nullptr;
+#ifdef JAVASCRIPT_ENABLED
+#include "webrtc_peer_connection_js.h"
+#else
+#include "webrtc_peer_connection_extension.h"
+#endif
-Ref<WebRTCPeerConnection> WebRTCPeerConnection::create_ref() {
- return create();
+StringName WebRTCPeerConnection::default_extension;
+
+void WebRTCPeerConnection::set_default_extension(const StringName &p_extension) {
+ default_extension = p_extension;
}
WebRTCPeerConnection *WebRTCPeerConnection::create() {
- if (!_create) {
- return nullptr;
+#ifdef JAVASCRIPT_ENABLED
+ return memnew(WebRTCPeerConnectionJS);
+#else
+ if (default_extension == String()) {
+ WARN_PRINT_ONCE("No default WebRTC extension configured.");
+ return memnew(WebRTCPeerConnectionExtension);
}
- return _create();
+ Object *obj = ClassDB::instantiate(default_extension);
+ return Object::cast_to<WebRTCPeerConnectionExtension>(obj);
+#endif
}
void WebRTCPeerConnection::_bind_methods() {
diff --git a/modules/webrtc/webrtc_peer_connection.h b/modules/webrtc/webrtc_peer_connection.h
index fcfb9ae9ae..e2ef3e55ad 100644
--- a/modules/webrtc/webrtc_peer_connection.h
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -47,11 +47,15 @@ public:
STATE_CLOSED
};
+private:
+ static StringName default_extension;
+
protected:
static void _bind_methods();
- static WebRTCPeerConnection *(*_create)();
public:
+ static void set_default_extension(const StringName &p_name);
+
virtual ConnectionState get_connection_state() const = 0;
virtual Error initialize(Dictionary p_config = Dictionary()) = 0;
@@ -63,7 +67,6 @@ public:
virtual Error poll() = 0;
virtual void close() = 0;
- static Ref<WebRTCPeerConnection> create_ref();
static WebRTCPeerConnection *create();
WebRTCPeerConnection();
diff --git a/modules/webrtc/webrtc_peer_connection_extension.cpp b/modules/webrtc/webrtc_peer_connection_extension.cpp
new file mode 100644
index 0000000000..33288e66d6
--- /dev/null
+++ b/modules/webrtc/webrtc_peer_connection_extension.cpp
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* webrtc_peer_connection_extension.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "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");
+ GDVIRTUAL_BIND(_create_offer);
+ GDVIRTUAL_BIND(_set_remote_description, "p_type", "p_sdp");
+ GDVIRTUAL_BIND(_set_local_description, "p_type", "p_sdp");
+ GDVIRTUAL_BIND(_add_ice_candidate, "p_sdp_mid_name", "p_sdp_mline_index", "p_sdp_name");
+ GDVIRTUAL_BIND(_poll);
+ 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)) {
+ return (ConnectionState)state;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_get_connection_state is unimplemented!");
+ return STATE_DISCONNECTED;
+}
+
+Error WebRTCPeerConnectionExtension::initialize(Dictionary p_config) {
+ int err;
+ if (GDVIRTUAL_CALL(_initialize, p_config, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_initialize is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+Ref<WebRTCDataChannel> WebRTCPeerConnectionExtension::create_data_channel(String p_label, Dictionary p_options) {
+ Object *ret = nullptr;
+ if (GDVIRTUAL_CALL(_create_data_channel, p_label, p_options, ret)) {
+ WebRTCDataChannel *ch = Object::cast_to<WebRTCDataChannel>(ret);
+ ERR_FAIL_COND_V_MSG(ret && !ch, nullptr, "Returned object must be an instance of WebRTCDataChannel.");
+ return ch;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_data_channel is unimplemented!");
+ return nullptr;
+}
+
+Error WebRTCPeerConnectionExtension::create_offer() {
+ int err;
+ if (GDVIRTUAL_CALL(_create_offer, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_create_offer is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+Error WebRTCPeerConnectionExtension::set_local_description(String p_type, String p_sdp) {
+ int err;
+ if (GDVIRTUAL_CALL(_set_local_description, p_type, p_sdp, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_local_description is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+Error WebRTCPeerConnectionExtension::set_remote_description(String p_type, String p_sdp) {
+ int err;
+ if (GDVIRTUAL_CALL(_set_remote_description, p_type, p_sdp, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_set_remote_description is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+Error WebRTCPeerConnectionExtension::add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) {
+ int err;
+ if (GDVIRTUAL_CALL(_add_ice_candidate, p_sdp_mid_name, p_sdp_mline_index, p_sdp_name, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_add_ice_candidate is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+Error WebRTCPeerConnectionExtension::poll() {
+ int err;
+ if (GDVIRTUAL_CALL(_poll, err)) {
+ return (Error)err;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_poll is unimplemented!");
+ return ERR_UNCONFIGURED;
+}
+
+void WebRTCPeerConnectionExtension::close() {
+ if (GDVIRTUAL_CALL(_close)) {
+ return;
+ }
+ WARN_PRINT_ONCE("WebRTCPeerConnectionExtension::_close is unimplemented!");
+}
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.h b/modules/webrtc/webrtc_peer_connection_extension.h
index 578af0202f..b3c2039fc1 100644
--- a/modules/webrtc/webrtc_peer_connection_gdnative.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* webrtc_peer_connection_gdnative.h */
+/* webrtc_peer_connection_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,30 +28,23 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBRTC_PEER_CONNECTION_GDNATIVE_H
-#define WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#ifndef WEBRTC_PEER_CONNECTION_EXTENSION_H
+#define WEBRTC_PEER_CONNECTION_EXTENSION_H
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
-#include "modules/gdnative/include/net/godot_net.h"
#include "webrtc_peer_connection.h"
-class WebRTCPeerConnectionGDNative : public WebRTCPeerConnection {
- GDCLASS(WebRTCPeerConnectionGDNative, WebRTCPeerConnection);
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
+#include "core/variant/native_ptr.h"
+
+class WebRTCPeerConnectionExtension : public WebRTCPeerConnection {
+ GDCLASS(WebRTCPeerConnectionExtension, WebRTCPeerConnection);
protected:
static void _bind_methods();
- static WebRTCPeerConnection *_create();
-
-private:
- static const godot_net_webrtc_library *default_library;
- const godot_net_webrtc_peer_connection *interface;
public:
- static Error set_default_library(const godot_net_webrtc_library *p_library);
- static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionGDNative::_create; }
-
- void set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl);
+ void make_default();
virtual ConnectionState get_connection_state() const override;
@@ -60,14 +53,22 @@ public:
virtual Error create_offer() override;
virtual Error set_remote_description(String type, String sdp) override;
virtual Error set_local_description(String type, String sdp) override;
- virtual Error add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) override;
+ virtual Error add_ice_candidate(String p_sdp_mid_name, int p_sdp_mline_index, String p_sdp_name) override;
virtual Error poll() override;
virtual void close() override;
- WebRTCPeerConnectionGDNative();
- ~WebRTCPeerConnectionGDNative();
-};
+ /** GDExtension **/
+ GDVIRTUAL0RC(int, _get_connection_state);
+ GDVIRTUAL1R(int, _initialize, Dictionary);
+ GDVIRTUAL2R(Object *, _create_data_channel, String, Dictionary);
+ GDVIRTUAL0R(int, _create_offer);
+ GDVIRTUAL2R(int, _set_remote_description, String, String);
+ GDVIRTUAL2R(int, _set_local_description, String, String);
+ GDVIRTUAL3R(int, _add_ice_candidate, String, int, String);
+ GDVIRTUAL0R(int, _poll);
+ GDVIRTUAL0(_close);
-#endif // WEBRTC_GDNATIVE_ENABLED
+ WebRTCPeerConnectionExtension() {}
+};
-#endif // WEBRTC_PEER_CONNECTION_GDNATIVE_H
+#endif // WEBRTC_PEER_CONNECTION_EXTENSION_H
diff --git a/modules/webrtc/webrtc_peer_connection_gdnative.cpp b/modules/webrtc/webrtc_peer_connection_gdnative.cpp
deleted file mode 100644
index dcf78dfb73..0000000000
--- a/modules/webrtc/webrtc_peer_connection_gdnative.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/*************************************************************************/
-/* webrtc_peer_connection_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifdef WEBRTC_GDNATIVE_ENABLED
-
-#include "webrtc_peer_connection_gdnative.h"
-
-#include "core/io/resource_loader.h"
-#include "modules/gdnative/nativescript/nativescript.h"
-#include "webrtc_data_channel_gdnative.h"
-
-const godot_net_webrtc_library *WebRTCPeerConnectionGDNative::default_library = nullptr;
-
-Error WebRTCPeerConnectionGDNative::set_default_library(const godot_net_webrtc_library *p_lib) {
- if (default_library) {
- const godot_net_webrtc_library *old = default_library;
- default_library = nullptr;
- old->unregistered();
- }
- default_library = p_lib;
- return OK; // Maybe add version check and fail accordingly
-}
-
-WebRTCPeerConnection *WebRTCPeerConnectionGDNative::_create() {
- WebRTCPeerConnectionGDNative *obj = memnew(WebRTCPeerConnectionGDNative);
- ERR_FAIL_COND_V_MSG(!default_library, obj, "Default GDNative WebRTC implementation not defined.");
-
- // Call GDNative constructor
- Error err = (Error)default_library->create_peer_connection(obj);
- ERR_FAIL_COND_V_MSG(err != OK, obj, "GDNative default library constructor returned an error.");
-
- return obj;
-}
-
-void WebRTCPeerConnectionGDNative::_bind_methods() {
-}
-
-WebRTCPeerConnectionGDNative::WebRTCPeerConnectionGDNative() {
- interface = nullptr;
-}
-
-WebRTCPeerConnectionGDNative::~WebRTCPeerConnectionGDNative() {
-}
-
-Error WebRTCPeerConnectionGDNative::initialize(Dictionary p_config) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->initialize(interface->data, (const godot_dictionary *)&p_config);
-}
-
-Ref<WebRTCDataChannel> WebRTCPeerConnectionGDNative::create_data_channel(String p_label, Dictionary p_options) {
- ERR_FAIL_COND_V(interface == nullptr, nullptr);
- return (WebRTCDataChannel *)interface->create_data_channel(interface->data, p_label.utf8().get_data(), (const godot_dictionary *)&p_options);
-}
-
-Error WebRTCPeerConnectionGDNative::create_offer() {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->create_offer(interface->data);
-}
-
-Error WebRTCPeerConnectionGDNative::set_local_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->set_local_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
-}
-
-Error WebRTCPeerConnectionGDNative::set_remote_description(String p_type, String p_sdp) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->set_remote_description(interface->data, p_type.utf8().get_data(), p_sdp.utf8().get_data());
-}
-
-Error WebRTCPeerConnectionGDNative::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->add_ice_candidate(interface->data, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
-}
-
-Error WebRTCPeerConnectionGDNative::poll() {
- ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED);
- return (Error)interface->poll(interface->data);
-}
-
-void WebRTCPeerConnectionGDNative::close() {
- ERR_FAIL_COND(interface == nullptr);
- interface->close(interface->data);
-}
-
-WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionGDNative::get_connection_state() const {
- ERR_FAIL_COND_V(interface == nullptr, STATE_DISCONNECTED);
- return (ConnectionState)interface->get_connection_state(interface->data);
-}
-
-void WebRTCPeerConnectionGDNative::set_native_webrtc_peer_connection(const godot_net_webrtc_peer_connection *p_impl) {
- interface = p_impl;
-}
-
-#endif // WEBRTC_GDNATIVE_ENABLED
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index 0272e67f6f..d2beccaf03 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -63,9 +63,6 @@ private:
static void _on_error(void *p_obj);
public:
- static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); }
- static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; }
-
virtual ConnectionState get_connection_state() const;
virtual Error initialize(Dictionary configuration = Dictionary());
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index 4c022c43cf..63c941c4a8 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -18,12 +18,11 @@ elif env["builtin_wslay"]:
"wslay_net.c",
"wslay_event.c",
"wslay_queue.c",
- "wslay_stack.c",
"wslay_frame.c",
]
thirdparty_sources = [thirdparty_dir + s for s in thirdparty_sources]
- env_ws.Prepend(CPPPATH=[thirdparty_dir + "includes/"])
+ env_ws.Prepend(CPPPATH=[thirdparty_dir])
env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"])
if env["platform"] == "windows" or env["platform"] == "uwp":
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
index 1549a907b4..4b515c12a1 100644
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -5,9 +5,10 @@
</brief_description>
<description>
This class implements a WebSocket client compatible with any RFC 6455-compliant WebSocket server.
- This client can be optionally used as a network peer for the [MultiplayerAPI].
+ This client can be optionally used as a multiplayer peer for the [MultiplayerAPI].
After starting the client ([method connect_to_url]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
You will receive appropriate signals when connecting, disconnecting, or when new data is available.
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
</tutorials>
@@ -20,7 +21,7 @@
<argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray()" />
<description>
Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested.
- If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted.
+ If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a multiplayer peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted.
If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]).
You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request.
[b]Note:[/b] To avoid mixed content warnings or errors in HTML5, you may have to use a [code]url[/code] that starts with [code]wss://[/code] (secure) instead of [code]ws://[/code]. When doing so, make sure to use the fully qualified domain name that matches the one defined in the server's SSL certificate. Do not connect directly via the IP address for [code]wss://[/code] connections, as it won't match with the SSL certificate.
@@ -90,6 +91,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index cd41e9a1fb..8d8ab220e2 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -4,7 +4,8 @@
Base class for WebSocket server and client.
</brief_description>
<description>
- Base class for WebSocket server and client, allowing them to be used as network peer for the [MultiplayerAPI].
+ Base class for WebSocket server and client, allowing them to be used as multiplayer peer for the [MultiplayerAPI].
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
</tutorials>
@@ -30,10 +31,6 @@
</description>
</method>
</methods>
- <members>
- <member name="refuse_new_connections" type="bool" setter="set_refuse_new_connections" getter="is_refusing_new_connections" override="true" default="false" />
- <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="MultiplayerPeer.TransferMode" default="2" />
- </members>
<signals>
<signal name="peer_packet">
<argument index="0" name="peer_source" type="int" />
@@ -43,6 +40,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index ab7ef6c4d0..2f455c32fd 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -4,7 +4,7 @@
A class representing a specific WebSocket connection.
</brief_description>
<description>
- This class represent a specific WebSocket connection, you can do lower level operations with it.
+ This class represents a specific WebSocket connection, allowing you to do lower level operations with it.
You can choose to write to the socket in binary or text mode, and you can recognize the mode used for writing by the other peer.
</description>
<tutorials>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
index 90182de4c2..f901b089ea 100644
--- a/modules/websocket/doc_classes/WebSocketServer.xml
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -7,6 +7,7 @@
This class implements a WebSocket server that can also support the high-level multiplayer API.
After starting the server ([method listen]), you will need to [method MultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
[b]Note:[/b] Not available in HTML5 exports.
+ [b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
</description>
<tutorials>
</tutorials>
@@ -55,7 +56,7 @@
<description>
Starts listening on the given port.
You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested.
- If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a network peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted.
+ If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a multiplayer peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted.
If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.), on the [WebSocketPeer] returned via [code]get_peer(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]).
</description>
</method>
@@ -116,6 +117,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/websocket/editor_debugger_server_websocket.cpp b/modules/websocket/editor_debugger_server_websocket.cpp
index 2e61cbfc08..78a5fa50d8 100644
--- a/modules/websocket/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor_debugger_server_websocket.cpp
@@ -31,6 +31,8 @@
#include "editor_debugger_server_websocket.h"
#include "core/config/project_settings.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "modules/websocket/remote_debugger_peer_websocket.h"
@@ -48,11 +50,47 @@ void EditorDebuggerServerWebSocket::poll() {
server->poll();
}
-Error EditorDebuggerServerWebSocket::start() {
- int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+String EditorDebuggerServerWebSocket::get_uri() const {
+ return endpoint;
+}
+
+Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
+ // Default host and port
+ String bind_host = (String)EditorSettings::get_singleton()->get("network/debug/remote_host");
+ int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+
+ // Optionally override
+ if (!p_uri.is_empty() && p_uri != "ws://") {
+ String scheme, path;
+ Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+ ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
+ }
+
+ // Set up the server
+ server->set_bind_ip(bind_host);
Vector<String> compatible_protocols;
compatible_protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer.
- return server->listen(remote_port, compatible_protocols);
+
+ // Try listening on ports
+ const int max_attempts = 5;
+ for (int attempt = 1;; ++attempt) {
+ const Error err = server->listen(bind_port, compatible_protocols);
+ if (err == OK) {
+ break;
+ }
+ if (attempt >= max_attempts) {
+ EditorNode::get_log()->add_message(vformat("Cannot listen on port %d, remote debugging unavailable.", bind_port), EditorLog::MSG_TYPE_ERROR);
+ return err;
+ }
+ int last_port = bind_port++;
+ EditorNode::get_log()->add_message(vformat("Cannot listen on port %d, trying %d instead.", last_port, bind_port), EditorLog::MSG_TYPE_WARNING);
+ }
+
+ // Endpoint that the client should connect to
+ endpoint = vformat("ws://%s:%d", bind_host, bind_port);
+
+ return OK;
}
void EditorDebuggerServerWebSocket::stop() {
diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h
index d9543bb647..1e5ea66146 100644
--- a/modules/websocket/editor_debugger_server_websocket.h
+++ b/modules/websocket/editor_debugger_server_websocket.h
@@ -40,6 +40,7 @@ class EditorDebuggerServerWebSocket : public EditorDebuggerServer {
private:
Ref<WebSocketServer> server;
List<int> pending_peers;
+ String endpoint;
public:
static EditorDebuggerServer *create(const String &p_protocol);
@@ -47,12 +48,13 @@ public:
void _peer_connected(int p_peer, String p_protocol);
void _peer_disconnected(int p_peer, bool p_was_clean);
- void poll() override;
- Error start() override;
- void stop() override;
- bool is_active() const override;
- bool is_connection_available() const override;
- Ref<RemoteDebuggerPeer> take_connection() override;
+ virtual void poll() override;
+ virtual String get_uri() const override;
+ virtual Error start(const String &p_uri = "") override;
+ virtual void stop() override;
+ virtual bool is_active() const override;
+ virtual bool is_connection_available() const override;
+ virtual Ref<RemoteDebuggerPeer> take_connection() override;
EditorDebuggerServerWebSocket();
~EditorDebuggerServerWebSocket();
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index d7263dcf43..035d036b90 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -54,11 +54,14 @@ Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_strin
}
Error EMWSPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(_out_buf_size && ((uint64_t)godot_js_websocket_buffered_amount(peer_sock) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0;
- godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin);
+ if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin) != 0) {
+ return FAILED;
+ }
+
return OK;
}
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index f7a8944745..bf35c91c7f 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -129,7 +129,7 @@ void WebSocketClient::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate);
- ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate);
+ ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate", "cert"), &WebSocketClient::set_trusted_ssl_certificate);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate");
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index d04909c97d..2ca60a3b61 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef WEBSOCKETMACTOS_H
-#define WEBSOCKETMACTOS_H
+#ifndef WEBSOCKET_MACROS_H
+#define WEBSOCKET_MACROS_H
// Defaults per peer buffers, 1024 packets with a shared 65536 bytes payload.
#define DEF_PKT_SHIFT 10
@@ -65,4 +65,4 @@ public:\
protected:\
/* clang-format on */
-#endif // WEBSOCKETMACTOS_H
+#endif // WEBSOCKET_MACROS_H
diff --git a/modules/websocket/websocket_multiplayer_peer.cpp b/modules/websocket/websocket_multiplayer_peer.cpp
index 163cc7706b..e54bfbca12 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -105,23 +105,6 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer
//
// MultiplayerPeer
//
-void WebSocketMultiplayerPeer::set_transfer_channel(int p_channel) {
- // Websocket does not have channels.
-}
-
-int WebSocketMultiplayerPeer::get_transfer_channel() const {
- return 0;
-}
-
-void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
- // Websocket uses TCP, reliable
-}
-
-MultiplayerPeer::TransferMode WebSocketMultiplayerPeer::get_transfer_mode() const {
- // Websocket uses TCP, reliable
- return TRANSFER_MODE_RELIABLE;
-}
-
void WebSocketMultiplayerPeer::set_target_peer(int p_target_peer) {
_target_peer = p_target_peer;
}
@@ -137,14 +120,6 @@ int WebSocketMultiplayerPeer::get_unique_id() const {
return _peer_id;
}
-void WebSocketMultiplayerPeer::set_refuse_new_connections(bool p_enable) {
- _refusing = p_enable;
-}
-
-bool WebSocketMultiplayerPeer::is_refusing_new_connections() const {
- return _refusing;
-}
-
void WebSocketMultiplayerPeer::_send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id) {
ERR_FAIL_COND(!p_peer.is_valid());
ERR_FAIL_COND(!p_peer->is_connected_to_host());
@@ -173,8 +148,8 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
// Then send the server peer (which will trigger connection_succeded in client)
_send_sys(get_peer(p_peer_id), SYS_ADD, 1);
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- int32_t id = E->key();
+ for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ int32_t id = E.key;
if (p_peer_id == id) {
continue; // Skip the newly added peer (already confirmed)
}
@@ -187,8 +162,8 @@ void WebSocketMultiplayerPeer::_send_add(int32_t p_peer_id) {
}
void WebSocketMultiplayerPeer::_send_del(int32_t p_peer_id) {
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- int32_t id = E->key();
+ for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ int32_t id = E.key;
if (p_peer_id != id) {
_send_sys(get_peer(id), SYS_DEL, p_peer_id);
}
@@ -211,17 +186,17 @@ Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, cons
return OK; // Will not send to self
} else if (p_to == 0) {
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- if (E->key() != p_from) {
- E->get()->put_packet(p_buffer, p_buffer_size);
+ for (KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ if (E.key != p_from) {
+ E.value->put_packet(p_buffer, p_buffer_size);
}
}
return OK; // Sent to all but sender
} else if (p_to < 0) {
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- if (E->key() != p_from && E->key() != -p_to) {
- E->get()->put_packet(p_buffer, p_buffer_size);
+ for (KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ if (E.key != p_from && E.key != -p_to) {
+ E.value->put_packet(p_buffer, p_buffer_size);
}
}
return OK; // Sent to all but sender and excluded
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index 0fee196f41..380edf67ed 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -32,7 +32,7 @@
#define WEBSOCKET_MULTIPLAYER_PEER_H
#include "core/error/error_list.h"
-#include "core/io/multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_peer.h"
#include "core/templates/list.h"
#include "websocket_peer.h"
@@ -68,7 +68,6 @@ protected:
bool _is_multiplayer = false;
int _target_peer = 0;
int _peer_id = 0;
- int _refusing = false;
static void _bind_methods();
@@ -78,15 +77,9 @@ protected:
public:
/* MultiplayerPeer */
- void set_transfer_channel(int p_channel) override;
- int get_transfer_channel() const override;
- void set_transfer_mode(TransferMode p_mode) override;
- TransferMode get_transfer_mode() const override;
void set_target_peer(int p_target_peer) override;
int get_packet_peer() const override;
int get_unique_id() const override;
- void set_refuse_new_connections(bool p_enable) override;
- bool is_refusing_new_connections() const override;
/* PacketPeer */
virtual int get_available_packet_count() const override;
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index fb838109f3..e7f90fdeba 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -50,19 +50,19 @@ void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_bind_ip"), &WebSocketServer::get_bind_ip);
- ClassDB::bind_method(D_METHOD("set_bind_ip"), &WebSocketServer::set_bind_ip);
+ ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &WebSocketServer::set_bind_ip);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bind_ip"), "set_bind_ip", "get_bind_ip");
ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key);
- ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key);
+ ClassDB::bind_method(D_METHOD("set_private_key", "key"), &WebSocketServer::set_private_key);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", PROPERTY_USAGE_NONE), "set_private_key", "get_private_key");
ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate);
- ClassDB::bind_method(D_METHOD("set_ssl_certificate"), &WebSocketServer::set_ssl_certificate);
+ ClassDB::bind_method(D_METHOD("set_ssl_certificate", "cert"), &WebSocketServer::set_ssl_certificate);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ssl_certificate", "get_ssl_certificate");
ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain);
- ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain);
+ ClassDB::bind_method(D_METHOD("set_ca_chain", "ca_chain"), &WebSocketServer::set_ca_chain);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", PROPERTY_USAGE_NONE), "set_ca_chain", "get_ca_chain");
ClassDB::bind_method(D_METHOD("get_handshake_timeout"), &WebSocketServer::get_handshake_timeout);
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index 7f027e1c0d..fc520ec57c 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -242,15 +242,15 @@ void WSLPeer::poll() {
Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
ERR_FAIL_COND_V(_out_pkt_size && (wslay_event_get_queued_msg_count(_data->ctx) >= (1ULL << _out_pkt_size)), ERR_OUT_OF_MEMORY);
- ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
+ ERR_FAIL_COND_V(_out_buf_size && (wslay_event_get_queued_msg_length(_data->ctx) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
- struct wslay_event_msg msg; // Should I use fragmented?
+ struct wslay_event_msg msg;
msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
msg.msg = p_buffer;
msg.msg_length = p_buffer_size;
- wslay_event_queue_msg(_data->ctx, &msg);
- if (wslay_event_send(_data->ctx) < 0) {
+ // Queue & send message.
+ if (wslay_event_queue_msg(_data->ctx, &msg) != 0 || wslay_event_send(_data->ctx) != 0) {
close_now();
return FAILED;
}
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index 7402bbb46e..a3d0d3554f 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -83,11 +83,11 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, St
break;
}
// Found a protocol
- if (protocol != "") {
+ if (!protocol.is_empty()) {
break;
}
}
- if (protocol == "") { // Invalid protocol(s) requested
+ if (protocol.is_empty()) { // Invalid protocol(s) requested
return false;
}
} else if (p_protocols.size() > 0) { // No protocol requested, but we need one
@@ -138,7 +138,7 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uin
s += "Upgrade: websocket\r\n";
s += "Connection: Upgrade\r\n";
s += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n";
- if (protocol != "") {
+ if (!protocol.is_empty()) {
s += "Sec-WebSocket-Protocol: " + protocol + "\r\n";
}
s += "\r\n";
@@ -182,12 +182,12 @@ Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp
void WSLServer::poll() {
List<int> remove_ids;
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ Ref<WSLPeer> peer = (WSLPeer *)E.value.ptr();
peer->poll();
if (!peer->is_connected_to_host()) {
- _on_disconnect(E->key(), peer->close_code != -1);
- remove_ids.push_back(E->key());
+ _on_disconnect(E.key, peer->close_code != -1);
+ remove_ids.push_back(E.key);
}
}
for (int &E : remove_ids) {
@@ -265,8 +265,8 @@ int WSLServer::get_max_packet_size() const {
void WSLServer::stop() {
_server->stop();
- for (Map<int, Ref<WebSocketPeer>>::Element *E = _peer_map.front(); E; E = E->next()) {
- Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ for (const KeyValue<int, Ref<WebSocketPeer>> &E : _peer_map) {
+ Ref<WSLPeer> peer = (WSLPeer *)E.value.ptr();
peer->close_now();
}
_pending.clear();
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
index 93c8bd9604..508b5a12a1 100644
--- a/modules/websocket/wsl_server.h
+++ b/modules/websocket/wsl_server.h
@@ -53,7 +53,7 @@ private:
Ref<StreamPeer> connection;
bool use_ssl = false;
- int time = 0;
+ uint64_t time = 0;
uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {};
int req_pos = 0;
String key;
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
index ff7c46bbae..6e224a8242 100644
--- a/modules/webxr/doc_classes/WebXRInterface.xml
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -87,7 +87,7 @@
There are several ways to handle "controller" input:
- Using [XRController3D] nodes and their [signal XRController3D.button_pressed] and [signal XRController3D.button_released] signals. This is how controllers are typically handled in AR/VR apps in Godot, however, this will only work with advanced VR controllers like the Oculus Touch or Index controllers, for example. The buttons codes are defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- Using [method Node._unhandled_input] and [InputEventJoypadButton] or [InputEventJoypadMotion]. This works the same as normal joypads, except the [member InputEvent.device] starts at 100, so the left controller is 100 and the right controller is 101, and the button codes are also defined by [url=https://immersive-web.github.io/webxr-gamepads-module/#xr-standard-gamepad-mapping]Section 3.3 of the WebXR Gamepads Module[/url].
- - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself. The [code]controller_id[/code] passed to these signals is the same id as used in [member XRController3D.controller_id].
+ - Using the [signal select], [signal squeeze] and related signals. This method will work for both advanced VR controllers, and non-traditional "controllers" like a tap on the screen, a spoken voice command or a button press on the device itself.
You can use one or all of these methods to allow your game or app to support a wider or narrower set of devices and input methods, or to allow more advanced interactions with more advanced devices.
</description>
<tutorials>
@@ -239,6 +239,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp
index 078a6547cf..16b483c39e 100644
--- a/modules/webxr/register_types.cpp
+++ b/modules/webxr/register_types.cpp
@@ -33,15 +33,34 @@
#include "webxr_interface.h"
#include "webxr_interface_js.h"
+#ifdef JAVASCRIPT_ENABLED
+Ref<WebXRInterfaceJS> webxr;
+#endif
+
void register_webxr_types() {
GDREGISTER_VIRTUAL_CLASS(WebXRInterface);
#ifdef JAVASCRIPT_ENABLED
- Ref<WebXRInterfaceJS> webxr;
webxr.instantiate();
XRServer::get_singleton()->add_interface(webxr);
#endif
}
void unregister_webxr_types() {
+#ifdef JAVASCRIPT_ENABLED
+ if (webxr.is_valid()) {
+ // uninitialise our interface if it is initialised
+ if (webxr->is_initialized()) {
+ webxr->uninitialize();
+ }
+
+ // unregister our interface from the XR server
+ if (XRServer::get_singleton()) {
+ XRServer::get_singleton()->remove_interface(webxr);
+ }
+
+ // and release
+ webxr.unref();
+ }
+#endif
}
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
index 2d699961ae..2676b3cf80 100644
--- a/modules/webxr/webxr_interface_js.cpp
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -163,9 +163,14 @@ String WebXRInterfaceJS::get_reference_space_type() const {
Ref<XRPositionalTracker> WebXRInterfaceJS::get_controller(int p_controller_id) const {
XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, nullptr);
+ ERR_FAIL_NULL_V(xr_server, Ref<XRPositionalTracker>());
- return xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
+ // TODO support more then two controllers
+ if (p_controller_id >= 0 && p_controller_id < 2) {
+ return controllers[p_controller_id];
+ };
+
+ return Ref<XRPositionalTracker>();
}
String WebXRInterfaceJS::get_visibility_state() const {
@@ -224,6 +229,13 @@ bool WebXRInterfaceJS::initialize() {
return false;
}
+ // we must create a tracker for our head
+ head_tracker.instantiate();
+ head_tracker->set_tracker_type(XRServer::TRACKER_HEAD);
+ head_tracker->set_tracker_name("head");
+ head_tracker->set_tracker_desc("Players head");
+ xr_server->add_tracker(head_tracker);
+
// make this our primary interface
xr_server->set_primary_interface(this);
@@ -254,9 +266,17 @@ bool WebXRInterfaceJS::initialize() {
void WebXRInterfaceJS::uninitialize() {
if (initialized) {
XRServer *xr_server = XRServer::get_singleton();
- if (xr_server != nullptr && xr_server->get_primary_interface() == this) {
- // no longer our primary interface
- xr_server->set_primary_interface(nullptr);
+ if (xr_server != nullptr) {
+ if (head_tracker.is_valid()) {
+ xr_server->remove_tracker(head_tracker);
+
+ head_tracker.unref();
+ }
+
+ if (xr_server->get_primary_interface() == this) {
+ // no longer our primary interface
+ xr_server->set_primary_interface(nullptr);
+ }
}
godot_webxr_uninitialize();
@@ -341,7 +361,7 @@ Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Tran
return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
};
-CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
+CameraMatrix WebXRInterfaceJS::get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) {
CameraMatrix eye;
float *js_matrix = godot_webxr_get_projection_for_eye(p_view + 1);
@@ -373,9 +393,9 @@ Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const R
}
// @todo Refactor this to be based on "views" rather than "eyes".
- godot_webxr_commit_for_eye(XRInterface::EYE_LEFT);
+ godot_webxr_commit_for_eye(1);
if (godot_webxr_get_view_count() > 1) {
- godot_webxr_commit_for_eye(XRInterface::EYE_RIGHT);
+ godot_webxr_commit_for_eye(2);
}
return blit_to_screen;
@@ -385,6 +405,11 @@ void WebXRInterfaceJS::process() {
if (initialized) {
godot_webxr_sample_controller_data();
+ if (head_tracker.is_valid()) {
+ // TODO set default pose to our head location (i.e. get_camera_transform without world scale and reference frame applied)
+ // head_tracker->set_pose("default", head_transform, Vector3(), Vector3());
+ }
+
int controller_count = godot_webxr_get_controller_count();
if (controller_count == 0) {
return;
@@ -400,51 +425,70 @@ void WebXRInterfaceJS::_update_tracker(int p_controller_id) {
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL(xr_server);
- Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id + 1);
+ // need to support more then two controllers...
+ if (p_controller_id < 0 || p_controller_id > 1) {
+ return;
+ }
+
+ Ref<XRPositionalTracker> tracker = controllers[p_controller_id];
if (godot_webxr_is_controller_connected(p_controller_id)) {
if (tracker.is_null()) {
tracker.instantiate();
tracker->set_tracker_type(XRServer::TRACKER_CONTROLLER);
// Controller id's 0 and 1 are always the left and right hands.
if (p_controller_id < 2) {
- tracker->set_tracker_name(p_controller_id == 0 ? "Left" : "Right");
+ tracker->set_tracker_name(p_controller_id == 0 ? "left_hand" : "right_hand");
+ tracker->set_tracker_desc(p_controller_id == 0 ? "Left hand controller" : "Right hand controller");
tracker->set_tracker_hand(p_controller_id == 0 ? XRPositionalTracker::TRACKER_HAND_LEFT : XRPositionalTracker::TRACKER_HAND_RIGHT);
+ } else {
+ char name[1024];
+ sprintf(name, "tracker_%i", p_controller_id);
+ tracker->set_tracker_name(name);
+ tracker->set_tracker_desc(name);
}
- // Use the ids we're giving to our "virtual" gamepads.
- tracker->set_joy_id(p_controller_id + 100);
xr_server->add_tracker(tracker);
}
- Input *input = Input::get_singleton();
-
float *tracker_matrix = godot_webxr_get_controller_transform(p_controller_id);
if (tracker_matrix) {
+ // Note, poses should NOT have world scale and our reference frame applied!
Transform3D transform = _js_matrix_to_transform(tracker_matrix);
- tracker->set_position(transform.origin);
- tracker->set_orientation(transform.basis);
+ tracker->set_pose("default", transform, Vector3(), Vector3());
free(tracker_matrix);
}
+ // TODO implement additional poses such as "aim" and "grip"
+
int *buttons = godot_webxr_get_controller_buttons(p_controller_id);
if (buttons) {
+ // TODO buttons should be named properly, this is just a temporary fix
for (int i = 0; i < buttons[0]; i++) {
- input->joy_button(p_controller_id + 100, (JoyButton)i, *((float *)buttons + (i + 1)));
+ char name[1024];
+ sprintf(name, "button_%i", i);
+
+ float value = *((float *)buttons + (i + 1));
+ bool state = value > 0.0;
+ tracker->set_input(name, state);
}
free(buttons);
}
int *axes = godot_webxr_get_controller_axes(p_controller_id);
if (axes) {
+ // TODO again just a temporary fix, split these between proper float and vector2 inputs
for (int i = 0; i < axes[0]; i++) {
- Input::JoyAxisValue joy_axis;
- joy_axis.min = -1;
- joy_axis.value = *((float *)axes + (i + 1));
- input->joy_axis(p_controller_id + 100, (JoyAxis)i, joy_axis);
+ char name[1024];
+ sprintf(name, "axis_%i", i);
+
+ float value = *((float *)axes + (i + 1));
+ ;
+ tracker->set_input(name, value);
}
free(axes);
}
} else if (tracker.is_valid()) {
xr_server->remove_tracker(tracker);
+ controllers[p_controller_id].unref();
}
}
@@ -454,7 +498,7 @@ void WebXRInterfaceJS::_on_controller_changed() {
for (int i = 0; i < 2; i++) {
bool controller_connected = godot_webxr_is_controller_connected(i);
if (controllers_state[i] != controller_connected) {
- Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", "");
+ // Input::get_singleton()->joy_connection_changed(i + 100, controller_connected, i == 0 ? "Left" : "Right", "");
controllers_state[i] = controller_connected;
}
}
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
index 82307190db..6e6548c946 100644
--- a/modules/webxr/webxr_interface_js.h
+++ b/modules/webxr/webxr_interface_js.h
@@ -46,6 +46,7 @@ class WebXRInterfaceJS : public WebXRInterface {
private:
bool initialized;
+ Ref<XRPositionalTracker> head_tracker;
String session_mode;
String required_features;
@@ -53,7 +54,9 @@ private:
String requested_reference_space_types;
String reference_space_type;
+ // TODO maybe turn into a vector to support more then 2 controllers...
bool controllers_state[2];
+ Ref<XRPositionalTracker> controllers[2];
Size2 render_targetsize;
Transform3D _js_matrix_to_transform(float *p_js_matrix);
@@ -86,7 +89,7 @@ public:
virtual uint32_t get_view_count() override;
virtual Transform3D get_camera_transform() override;
virtual Transform3D get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
+ virtual CameraMatrix get_projection_for_view(uint32_t p_view, double p_aspect, double p_z_near, double p_z_far) override;
virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override;
virtual void process() override;
diff --git a/modules/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index 8913ef1b65..58a6216b1e 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -114,7 +114,7 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
input_mesh.vertexPositionStride = sizeof(float) * 3;
input_mesh.vertexNormalData = p_normals;
input_mesh.vertexNormalStride = sizeof(uint32_t) * 3;
- input_mesh.vertexUvData = NULL;
+ input_mesh.vertexUvData = nullptr;
input_mesh.vertexUvStride = 0;
xatlas::ChartOptions chart_options;